From: Petter Reinholdtsen Date: Tue, 29 Apr 2025 05:35:00 +0000 (+0200) Subject: Import opensnitch_1.6.9.orig.tar.gz X-Git-Tag: archive/raspbian/1.6.9-3+rpi1~9 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/%22/%22http:/www.example.com/cgi/%22?a=commitdiff_plain;h=9602346033e5191d3ddae0bacc9ace2919ba868e;p=opensnitch.git Import opensnitch_1.6.9.orig.tar.gz [dgit import orig opensnitch_1.6.9.orig.tar.gz] --- 9602346033e5191d3ddae0bacc9ace2919ba868e diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..815daa9 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: gustavo-iniguez-goya +patreon: # Replace with a single patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..3d2b6f3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,60 @@ +--- +name: 🐞 Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +Please, check the FAQ and Known Problems pages before creating the bug report: + +https://github.com/evilsocket/opensnitch/wiki/FAQs + +GUI related issues: +https://github.com/evilsocket/opensnitch/wiki/GUI-known-problems + +Daemon related issues: + - Run `opensnitchd -check-requirements` to see if your kernel is compatible. + - https://github.com/evilsocket/opensnitch/wiki/daemon-known-problems + +**Describe the bug** +A clear and concise description of what the bug is. + +Include the following information: + - OpenSnitch version. + - OS: [e.g. Debian GNU/Linux, ArchLinux, Slackware, ...] + - Version [e.g. Buster, 10.3, 20.04] + - Window Manager: [e.g. GNOME Shell, KDE, enlightenment, i3wm, ...] + - Kernel version: echo $(uname -a) + +**To Reproduce** +Describe in detail as much as you can what happened. + +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Post error logs:** +If it's a crash of the GUI: + - Launch it from a terminal and reproduce the issue. + - Post the errors logged to the terminal. + +If the daemon doesn't start or doesn't intercept connections: + - Run `opensnitchd -check-requirements` to see if your kernel is compatible. + - Post last 15 lines of the log file `/var/log/opensnitchd.log` + - Or launch it from a terminal as root (`# /usr/bin/opensnitchd -rules-path /etc/opensnitchd/rules`) and post the errors logged to the terminal. + +If the deb or rpm packages fail to install: + - Install them from a terminal (`$ sudo dpkg -i opensnitch*` / `$ sudo yum install opensnitch*`), and post the errors logged to stdout. + +**Expected behavior (optional)** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots or videos to help explain your problem. It may help to understand the issue much better. + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..dd3c313 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,4 @@ +contact_links: + - name: 🙋 Question + url: https://github.com/evilsocket/opensnitch/discussions/new + about: Ask your question here diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md new file mode 100644 index 0000000..7e54dce --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.md @@ -0,0 +1,15 @@ +--- +name: 💡 Feature request +about: Suggest an idea +title: '[Feature Request] ' +labels: feature +assignees: '' + +--- + +<!-- +Note: Please, use the search box to see if this feature has already been requested. +--> + +### Summary: +<!-- A concise description of the new feature. --> diff --git a/.github/workflows/build_ebpf_modules.yml b/.github/workflows/build_ebpf_modules.yml new file mode 100644 index 0000000..1b65f69 --- /dev/null +++ b/.github/workflows/build_ebpf_modules.yml @@ -0,0 +1,59 @@ +# This is a basic workflow to help you get started with Actions + +name: CI - build eBPF modules + +# Controls when the workflow will run +on: + # Triggers the workflow on push or pull request events but only for the "master" branch + push: + paths: + - 'ebpf_prog/*' + - '.github/workflows/build_ebpf_modules.yml' + pull_request: + paths: + - 'ebpf_prog/*' + - '.github/workflows/build_ebpf_modules.yml' + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + + # This workflow contains a single job called "build" + # The matrix configuration will execute the steps, once per dimension defined: + # kernel 5.8 + tag 1.5.0 + # kernel 5.8 + tag master + # kernel 6.0 + tag 1.5.0, etc + build: + strategy: + matrix: + kernel: ["6.0"] + tag: ["1.6.0"] + + runs-on: ubuntu-22.04 + + steps: + - name: Check out code into the Go module directory + uses: actions/checkout@v3 + with: + # ref: can be a branch name, tag, commit, etc + ref: ${{ matrix.tag }} + + - name: Get dependencies + run: | + sudo apt-get install git dpkg-dev rpm flex bison ca-certificates wget python3 rsync bc libssl-dev clang llvm libelf-dev libzip-dev git libnetfilter-queue-dev libpcap-dev protobuf-compiler python3-pip dh-golang golang-any golang-golang-x-net-dev golang-google-grpc-dev golang-goprotobuf-dev libmnl-dev golang-github-vishvananda-netlink-dev golang-github-evilsocket-ftrace-dev golang-github-google-gopacket-dev golang-github-fsnotify-fsnotify-dev linux-headers-$(uname -r) + - name: Download kernel sources and compile eBPF modules + run: | + kernel_version="${{ matrix.kernel }}" + if [ ! -d utils/packaging/ ]; then + mkdir -p utils/packaging/ + fi + wget https://raw.githubusercontent.com/evilsocket/opensnitch/master/utils/packaging/build_modules.sh -O utils/packaging/build_modules.sh + bash utils/packaging/build_modules.sh $kernel_version + sha1sum ebpf_prog/modules/opensnitch*o > ebpf_prog/modules/checksums.txt + + - uses: actions/upload-artifact@v4 + with: + name: opensnitch-ebpf-modules-${{ matrix.kernel }}-${{ matrix.tag }} + path: ebpf_prog/modules/* diff --git a/.github/workflows/generic_validations.yml b/.github/workflows/generic_validations.yml new file mode 100644 index 0000000..a8cec13 --- /dev/null +++ b/.github/workflows/generic_validations.yml @@ -0,0 +1,37 @@ +name: Test resources validation +on: + + # Trigger this workflow only when ebpf modules changes. + push: + paths: + - 'ui/resources/*' + - '.github/workflows/generic.yml' + pull_request: + paths: + - 'ui/resources/*' + - '.github/workflows/generic.yml' + + # Allow to run this workflow manually from the Actions tab + workflow_dispatch: + +jobs: + + build: + name: Install tools + runs-on: ubuntu-latest + steps: + + - name: Check out git code + uses: actions/checkout@v2 + + - name: Get and prepare dependencies + run: | + set -e + set -x + sudo apt install desktop-file-utils appstream + - name: Validate resources + run: | + set -e + set -x + desktop-file-validate ui/resources/opensnitch_ui.desktop + appstreamcli validate ui/resources/io.github.evilsocket.opensnitch.appdata.xml diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml new file mode 100644 index 0000000..307b9d3 --- /dev/null +++ b/.github/workflows/go.yml @@ -0,0 +1,49 @@ +name: Build status +on: + push: + paths: + - 'daemon/**' + - '.github/workflows/go.yml' + pull_request: + paths: + - 'daemon/**' + - '.github/workflows/go.yml' + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +jobs: + build: + name: Build + runs-on: ubuntu-latest + steps: + - name: Set up Go 1.20.10 + uses: actions/setup-go@v3 + with: + go-version: 1.20.10 + id: go + + - name: Check out code into the Go module directory + uses: actions/checkout@v3 + + - name: Get dependencies + run: | + sudo apt-get install git libnetfilter-queue-dev libmnl-dev libpcap-dev protobuf-compiler + export GOPATH=~/go + export PATH=$PATH:$GOPATH/bin + go install github.com/golang/protobuf/protoc-gen-go@latest + go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.34.1 + go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.3.0 + cd proto + make ../daemon/ui/protocol/ui.pb.go + cd ../daemon + go mod tidy; go mod vendor + + - name: Build + run: | + cd daemon + go build -v . + - name: Test + run: | + cd daemon + sudo PRIVILEGED_TESTS=1 NETLINK_TESTS=1 go test ./... diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8546be8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.sock +*.pyc +*.profile diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<https://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<https://www.gnu.org/licenses/why-not-lgpl.html>. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..96d668c --- /dev/null +++ b/Makefile @@ -0,0 +1,47 @@ +all: protocol opensnitch_daemon gui + +install: + @cd daemon && make install + @cd ui && make install + +protocol: + @cd proto && make + +opensnitch_daemon: + @cd daemon && make + +gui: + @cd ui && make + +clean: + @cd daemon && make clean + @cd proto && make clean + @cd ui && make clean + +run: + cd ui && pip3 install --upgrade . && cd .. + opensnitch-ui --socket unix:///tmp/osui.sock & + ./daemon/opensnitchd -rules-path /etc/opensnitchd/rules -ui-socket unix:///tmp/osui.sock -cpu-profile cpu.profile -mem-profile mem.profile + +test: + clear + make clean + clear + mkdir -p rules + make + clear + make run + +adblocker: + clear + make clean + clear + make + clear + python make_ads_rules.py + clear + cd ui && pip3 install --upgrade . && cd .. + opensnitch-ui --socket unix:///tmp/osui.sock & + ./daemon/opensnitchd -rules-path /etc/opensnitchd/rules -ui-socket unix:///tmp/osui.sock + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..16d25c4 --- /dev/null +++ b/README.md @@ -0,0 +1,77 @@ +<p align="center"> + <img alt="opensnitch" src="https://raw.githubusercontent.com/evilsocket/opensnitch/master/ui/opensnitch/res/icon.png" height="160" /> + <p align="center"> + <img src="https://github.com/evilsocket/opensnitch/workflows/Build%20status/badge.svg" /> + <a href="https://github.com/evilsocket/opensnitch/releases/latest"><img alt="Release" src="https://img.shields.io/github/release/evilsocket/opensnitch.svg?style=flat-square"></a> + <a href="https://github.com/evilsocket/opensnitch/blob/master/LICENSE.md"><img alt="Software License" src="https://img.shields.io/badge/license-GPL3-brightgreen.svg?style=flat-square"></a> + <a href="https://goreportcard.com/report/github.com/evilsocket/opensnitch/daemon"><img alt="Go Report Card" src="https://goreportcard.com/badge/github.com/evilsocket/opensnitch/daemon?style=flat-square"></a> + <a href="https://repology.org/project/opensnitch/versions"><img src="https://repology.org/badge/tiny-repos/opensnitch.svg" alt="Packaging status"></a> + </p> +</p> + +<p align="center"><strong>OpenSnitch</strong> is a GNU/Linux application firewall.</p> + +<p align="center">•• <a href="#key-features">Key Features</a> • <a href="#download">Download</a> • <a href="#installation">Installation</a> • <a href="#opensnitch-in-action">Usage examples</a> • <a href="#in-the-press">In the press</a> ••</p> + +<p align="center"> + <img src="https://user-images.githubusercontent.com/2742953/85205382-6ba9cb00-b31b-11ea-8e9a-bd4b8b05a236.png" alt="OpenSnitch"/> +</p> + +## Key features + * Interactive outbound connections filtering. + * [Block ads, trackers or malware domains](https://github.com/evilsocket/opensnitch/wiki/block-lists) system wide. + * Ability to [configure system firewall](https://github.com/evilsocket/opensnitch/wiki/System-rules) from the GUI (nftables). + - Configure input policy, allow inbound services, etc. + * Manage [multiple nodes](https://github.com/evilsocket/opensnitch/wiki/Nodes) from a centralized GUI. + * [SIEM integration](https://github.com/evilsocket/opensnitch/wiki/SIEM-integration) + +## Download + +Download deb/rpm packages for your system from https://github.com/evilsocket/opensnitch/releases + +## Installation + +#### deb +> $ sudo apt install ./opensnitch*.deb ./python3-opensnitch-ui*.deb + +#### rpm +> $ sudo yum localinstall opensnitch-1*.rpm; sudo yum localinstall opensnitch-ui*.rpm + +Then run: `$ opensnitch-ui` or launch the GUI from the Applications menu. + +Please, refer to [the documentation](https://github.com/evilsocket/opensnitch/wiki/Installation) for detailed information. + +## OpenSnitch in action + +Examples of OpenSnitch intercepting unexpected connections: + +https://github.com/evilsocket/opensnitch/discussions/categories/show-and-tell + +Have you seen a connection you didn't expect? [submit it!](https://github.com/evilsocket/opensnitch/discussions/new?category=show-and-tell) + +## In the press + +- 2017 [PenTest Magazine](https://twitter.com/pentestmag/status/857321886807605248) +- 11/2019 [It's Foss](https://itsfoss.com/opensnitch-firewall-linux/) +- 03/2020 [Linux Format #232](https://www.linux-magazine.com/Issues/2020/232/Firewalld-and-OpenSnitch) +- 08/2020 [Linux Magazine Polska #194](https://linux-magazine.pl/archiwum/wydanie/387) +- 08/2021 [Linux Format #280](https://github.com/evilsocket/opensnitch/discussions/631) +- 02/2022 [Linux User](https://www.linux-community.de/magazine/linuxuser/2022/03/) +- 06/2022 [Linux Magazine #259](https://www.linux-magazine.com/Issues/2022/259/OpenSnitch) + +## Donations + +If you find OpenSnitch useful and want to donate to the dedicated developers, you can do it from the **Sponsor this project** section on the right side of this repository. + +You can see here who are the current maintainers of OpenSnitch: +https://github.com/evilsocket/opensnitch/commits/master + +## Contributors + +[See the list](https://github.com/evilsocket/opensnitch/graphs/contributors) + +## Translating + +<a href="https://hosted.weblate.org/engage/opensnitch/"> +<img src="https://hosted.weblate.org/widgets/opensnitch/-/glossary/multi-auto.svg" alt="Translation status" /> +</a> diff --git a/daemon/.gitignore b/daemon/.gitignore new file mode 100644 index 0000000..ac08621 --- /dev/null +++ b/daemon/.gitignore @@ -0,0 +1,2 @@ +opensnitchd +vendor diff --git a/daemon/Gopkg.toml b/daemon/Gopkg.toml new file mode 100644 index 0000000..419b318 --- /dev/null +++ b/daemon/Gopkg.toml @@ -0,0 +1,19 @@ +[[constraint]] + name = "github.com/fsnotify/fsnotify" + version = "1.4.7" + +[[constraint]] + name = "github.com/google/gopacket" + version = "~1.1.14" + +[[constraint]] + name = "google.golang.org/grpc" + version = "~1.11.2" + +[[constraint]] + name = "github.com/evilsocket/ftrace" + version = "~1.2.0" + +[prune] + go-tests = true + unused-packages = true diff --git a/daemon/Makefile b/daemon/Makefile new file mode 100644 index 0000000..df537b3 --- /dev/null +++ b/daemon/Makefile @@ -0,0 +1,26 @@ +#SRC contains all *.go *.c *.h files in daemon/ and its subfolders +SRC := $(shell find . -type f -name '*.go' -o -name '*.h' -o -name '*.c') +PREFIX?=/usr/local + +all: opensnitchd + +install: + @mkdir -p $(DESTDIR)/etc/opensnitchd/rules + @install -Dm755 opensnitchd \ + -t $(DESTDIR)$(PREFIX)/bin/ + @install -Dm644 opensnitchd.service \ + -t $(DESTDIR)/etc/systemd/system/ + @install -Dm644 default-config.json \ + -t $(DESTDIR)/etc/opensnitchd/ + @install -Dm644 system-fw.json \ + -t $(DESTDIR)/etc/opensnitchd/ + @systemctl daemon-reload + +opensnitchd: $(SRC) + @go get + @go build -o opensnitchd . + +clean: + @rm -rf opensnitchd + + diff --git a/daemon/conman/connection.go b/daemon/conman/connection.go new file mode 100644 index 0000000..2f6dcf7 --- /dev/null +++ b/daemon/conman/connection.go @@ -0,0 +1,332 @@ +package conman + +import ( + "errors" + "fmt" + "net" + "os" + + "github.com/evilsocket/opensnitch/daemon/core" + "github.com/evilsocket/opensnitch/daemon/dns" + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/evilsocket/opensnitch/daemon/netfilter" + "github.com/evilsocket/opensnitch/daemon/netlink" + "github.com/evilsocket/opensnitch/daemon/netstat" + "github.com/evilsocket/opensnitch/daemon/procmon" + "github.com/evilsocket/opensnitch/daemon/procmon/audit" + "github.com/evilsocket/opensnitch/daemon/procmon/ebpf" + "github.com/evilsocket/opensnitch/daemon/ui/protocol" + + "github.com/google/gopacket/layers" +) + +// Connection represents an outgoing connection. +type Connection struct { + Entry *netstat.Entry + Process *procmon.Process + Pkt *netfilter.Packet + Protocol string + DstHost string + SrcIP net.IP + DstIP net.IP + SrcPort uint + DstPort uint +} + +var showUnknownCons = false + +// Parse extracts the IP layers from a network packet to determine what +// process generated a connection. +func Parse(nfp netfilter.Packet, interceptUnknown bool) *Connection { + showUnknownCons = interceptUnknown + + if nfp.IsIPv4() { + con, err := NewConnection(&nfp) + if err != nil { + log.Debug("%s", err) + return nil + } else if con == nil { + return nil + } + return con + } + + if core.IPv6Enabled == false { + return nil + } + con, err := NewConnection6(&nfp) + if err != nil { + log.Debug("%s", err) + return nil + } else if con == nil { + return nil + } + return con + +} + +func newConnectionImpl(nfp *netfilter.Packet, c *Connection, protoType string) (cr *Connection, err error) { + // no errors but not enough info neither + if c.parseDirection(protoType) == false { + return nil, nil + } + log.Debug("new connection %s => %d:%v -> %v (%s):%d uid: %d, mark: %x", c.Protocol, c.SrcPort, c.SrcIP, c.DstIP, c.DstHost, c.DstPort, nfp.UID, nfp.Mark) + + c.Entry = &netstat.Entry{ + Proto: c.Protocol, + SrcIP: c.SrcIP, + SrcPort: c.SrcPort, + DstIP: c.DstIP, + DstPort: c.DstPort, + UserId: -1, + INode: -1, + } + + pid := -1 + uid := -1 + if procmon.MethodIsEbpf() { + swap := false + c.Process, swap, err = ebpf.GetPid(c.Protocol, c.SrcPort, c.SrcIP, c.DstIP, c.DstPort) + if swap { + c.swapFields() + } + + if c.Process != nil { + c.Entry.UserId = c.Process.UID + return c, nil + } + if err != nil { + log.Debug("ebpf warning: %v", err) + return nil, nil + } + } else if procmon.MethodIsAudit() { + if aevent := audit.GetEventByPid(pid); aevent != nil { + audit.Lock.RLock() + c.Process = procmon.NewProcess(pid, aevent.ProcName) + c.Process.Path = aevent.ProcPath + c.Process.ReadCmdline() + c.Process.CWD = aevent.ProcDir + audit.Lock.RUnlock() + // if the proc dir contains non alhpa-numeric chars the field is empty + if c.Process.CWD == "" { + c.Process.ReadCwd() + } + c.Process.ReadEnv() + c.Process.CleanPath() + + procmon.AddToActivePidsCache(uint64(pid), c.Process) + return c, nil + } + } + + // Sometimes when using eBPF, the PID is not found by the connection's parameters, + // but falling back to legacy methods helps to find it and avoid "unknown/kernel pop-ups". + // + // One of the reasons is because after coming back from suspend state, for some reason (bug?), + // gobpf/libbpf is unable to delete ebpf map entries, so when they reach the maximum capacity no + // more entries are added, nor updated. + if pid < 0 { + // 0. lookup uid and inode via netlink. Can return several inodes. + // 1. lookup uid and inode using /proc/net/(udp|tcp|udplite) + // 2. lookup pid by inode + // 3. if this is coming from us, just accept + // 4. lookup process info by pid + var inodeList []int + uid, inodeList = netlink.GetSocketInfo(c.Protocol, c.SrcIP, c.SrcPort, c.DstIP, c.DstPort) + if len(inodeList) == 0 { + procmon.GetInodeFromNetstat(c.Entry, &inodeList, c.Protocol, c.SrcIP, c.SrcPort, c.DstIP, c.DstPort) + } + + for n, inode := range inodeList { + pid = procmon.GetPIDFromINode(inode, fmt.Sprint(inode, c.SrcIP, c.SrcPort, c.DstIP, c.DstPort)) + if pid != -1 { + log.Debug("[%d] PID found %d [%d]", n, pid, inode) + c.Entry.INode = inode + break + } + } + } + + if pid == os.Getpid() { + // return a Process object with our PID, to be able to exclude our own connections + // (to the UI on a local socket for example) + c.Process = procmon.NewProcess(pid, "") + return c, nil + } + + if nfp.UID != 0xffffffff { + uid = int(nfp.UID) + } + c.Entry.UserId = uid + + if c.Process == nil { + if c.Process = procmon.FindProcess(pid, showUnknownCons); c.Process == nil { + return nil, fmt.Errorf("Could not find process by its pid %d for: %s", pid, c) + } + } + + return c, nil +} + +// NewConnection creates a new Connection object, and returns the details of it. +func NewConnection(nfp *netfilter.Packet) (c *Connection, err error) { + ipv4 := nfp.Packet.Layer(layers.LayerTypeIPv4) + if ipv4 == nil { + return nil, errors.New("Error getting IPv4 layer") + } + ip, ok := ipv4.(*layers.IPv4) + if !ok { + return nil, errors.New("Error getting IPv4 layer data") + } + c = &Connection{ + SrcIP: ip.SrcIP, + DstIP: ip.DstIP, + DstHost: dns.HostOr(ip.DstIP, ""), + Pkt: nfp, + } + return newConnectionImpl(nfp, c, "") +} + +// NewConnection6 creates a IPv6 new Connection object, and returns the details of it. +func NewConnection6(nfp *netfilter.Packet) (c *Connection, err error) { + ipv6 := nfp.Packet.Layer(layers.LayerTypeIPv6) + if ipv6 == nil { + return nil, errors.New("Error getting IPv6 layer") + } + ip, ok := ipv6.(*layers.IPv6) + if !ok { + return nil, errors.New("Error getting IPv6 layer data") + } + c = &Connection{ + SrcIP: ip.SrcIP, + DstIP: ip.DstIP, + DstHost: dns.HostOr(ip.DstIP, ""), + Pkt: nfp, + } + return newConnectionImpl(nfp, c, "6") +} + +func (c *Connection) parseDirection(protoType string) bool { + ret := false + if tcpLayer := c.Pkt.Packet.Layer(layers.LayerTypeTCP); tcpLayer != nil { + if tcp, ok := tcpLayer.(*layers.TCP); ok == true && tcp != nil { + c.Protocol = "tcp" + protoType + c.DstPort = uint(tcp.DstPort) + c.SrcPort = uint(tcp.SrcPort) + ret = true + + if tcp.DstPort == 53 { + c.getDomains(c.Pkt, c) + } + } + } else if udpLayer := c.Pkt.Packet.Layer(layers.LayerTypeUDP); udpLayer != nil { + if udp, ok := udpLayer.(*layers.UDP); ok == true && udp != nil { + c.Protocol = "udp" + protoType + c.DstPort = uint(udp.DstPort) + c.SrcPort = uint(udp.SrcPort) + ret = true + + if udp.DstPort == 53 { + c.getDomains(c.Pkt, c) + } + } + } else if udpliteLayer := c.Pkt.Packet.Layer(layers.LayerTypeUDPLite); udpliteLayer != nil { + if udplite, ok := udpliteLayer.(*layers.UDPLite); ok == true && udplite != nil { + c.Protocol = "udplite" + protoType + c.DstPort = uint(udplite.DstPort) + c.SrcPort = uint(udplite.SrcPort) + ret = true + } + } else if sctpLayer := c.Pkt.Packet.Layer(layers.LayerTypeSCTP); sctpLayer != nil { + if sctp, ok := sctpLayer.(*layers.SCTP); ok == true && sctp != nil { + c.Protocol = "sctp" + protoType + c.DstPort = uint(sctp.DstPort) + c.SrcPort = uint(sctp.SrcPort) + ret = true + } + } else if icmpLayer := c.Pkt.Packet.Layer(layers.LayerTypeICMPv4); icmpLayer != nil { + if icmp, ok := icmpLayer.(*layers.ICMPv4); ok == true && icmp != nil { + c.Protocol = "icmp" + c.DstPort = 0 + c.SrcPort = 0 + ret = true + } + } else if icmp6Layer := c.Pkt.Packet.Layer(layers.LayerTypeICMPv6); icmp6Layer != nil { + if icmp6, ok := icmp6Layer.(*layers.ICMPv6); ok == true && icmp6 != nil { + c.Protocol = "icmp" + protoType + c.DstPort = 0 + c.SrcPort = 0 + ret = true + } + } + + return ret +} + +// swapFields swaps connection's fields. +// Used to workaround an issue where outbound connections +// have the fields swapped (procmon/ebpf/find.go). +func (c *Connection) swapFields() { + oEntry := c.Entry + c.Entry = &netstat.Entry{ + Proto: c.Protocol, + SrcIP: oEntry.DstIP, + DstIP: oEntry.SrcIP, + SrcPort: oEntry.DstPort, + DstPort: oEntry.SrcPort, + UserId: oEntry.UserId, + INode: oEntry.INode, + } + c.SrcIP = oEntry.DstIP + c.DstIP = oEntry.SrcIP + c.DstPort = oEntry.SrcPort + c.SrcPort = oEntry.DstPort +} + +func (c *Connection) getDomains(nfp *netfilter.Packet, con *Connection) { + domains := dns.GetQuestions(nfp) + if len(domains) < 1 { + return + } + for _, dns := range domains { + con.DstHost = dns + } +} + +// To returns the destination host of a connection. +func (c *Connection) To() string { + if c.DstHost == "" { + return c.DstIP.String() + } + return fmt.Sprintf("%s (%s)", c.DstHost, c.DstIP) +} + +func (c *Connection) String() string { + if c.Entry == nil { + return fmt.Sprintf("%d:%s ->(%s)-> %s:%d", c.SrcPort, c.SrcIP, c.Protocol, c.To(), c.DstPort) + } + + if c.Process == nil { + return fmt.Sprintf("%d:%s (uid:%d) ->(%s)-> %s:%d", c.SrcPort, c.SrcIP, c.Entry.UserId, c.Protocol, c.To(), c.DstPort) + } + + return fmt.Sprintf("%s (%d) -> %s:%d (proto:%s uid:%d)", c.Process.Path, c.Process.ID, c.To(), c.DstPort, c.Protocol, c.Entry.UserId) +} + +// Serialize returns a connection serialized. +func (c *Connection) Serialize() *protocol.Connection { + return &protocol.Connection{ + Protocol: c.Protocol, + SrcIp: c.SrcIP.String(), + SrcPort: uint32(c.SrcPort), + DstIp: c.DstIP.String(), + DstHost: c.DstHost, + DstPort: uint32(c.DstPort), + UserId: uint32(c.Entry.UserId), + ProcessId: uint32(c.Process.ID), + ProcessPath: c.Process.Path, + ProcessArgs: c.Process.Args, + ProcessEnv: c.Process.Env, + ProcessCwd: c.Process.CWD, + } +} diff --git a/daemon/conman/connection_test.go b/daemon/conman/connection_test.go new file mode 100644 index 0000000..4c76a1a --- /dev/null +++ b/daemon/conman/connection_test.go @@ -0,0 +1,127 @@ +package conman + +import ( + "fmt" + "net" + "testing" + + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + + "github.com/evilsocket/opensnitch/daemon/netfilter" +) + +// Adding new packets: +// wireshark -> right click -> Copy as HexDump -> create []byte{} + +func NewTCPPacket() gopacket.Packet { + // 47676:192.168.1.100 -> 1.1.1.1:23 + testTCPPacket := []byte{0x4c, 0x6e, 0x6e, 0xd5, 0x79, 0xbf, 0x00, 0x28, 0x9d, 0x43, 0x7f, 0xd7, 0x08, 0x00, 0x45, 0x10, + 0x00, 0x3c, 0x1d, 0x07, 0x40, 0x00, 0x40, 0x06, 0x59, 0x8e, 0xc0, 0xa8, 0x01, 0x6d, 0x01, 0x01, + 0x01, 0x01, 0xba, 0x3c, 0x00, 0x17, 0x47, 0x7e, 0xf3, 0x0b, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x02, + 0xfa, 0xf0, 0x4c, 0x27, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4, 0x04, 0x02, 0x08, 0x0a, 0x91, 0xfb, + 0xb5, 0xf4, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x0a} + return gopacket.NewPacket(testTCPPacket, layers.LinkTypeEthernet, gopacket.Default) +} + +func NewUDPPacket() gopacket.Packet { + // 29517:192.168.1.109 -> 1.0.0.1:53 + testUDPPacketDNS := []byte{ + 0x4c, 0x6e, 0x6e, 0xd5, 0x79, 0xbf, 0x00, 0x28, 0x9d, 0x43, 0x7f, 0xd7, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x40, 0x54, 0x1a, 0x40, 0x00, 0x3f, 0x11, 0x24, 0x7d, 0xc0, 0xa8, 0x01, 0x6d, 0x01, 0x00, + 0x00, 0x01, 0x73, 0x4d, 0x00, 0x35, 0x00, 0x2c, 0xf1, 0x17, 0x05, 0x51, 0x00, 0x20, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x70, 0x69, 0x04, 0x68, 0x6f, 0x6c, 0x65, 0x00, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x29, 0x10, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + } + + return gopacket.NewPacket(testUDPPacketDNS, layers.LinkTypeEthernet, gopacket.Default) +} + +func EstablishConnection(proto, dst string) (net.Conn, error) { + c, err := net.Dial(proto, dst) + if err != nil { + fmt.Println(err) + return nil, err + } + return c, nil +} + +func ListenOnPort(proto, port string) (net.Listener, error) { + l, err := net.Listen(proto, port) + if err != nil { + fmt.Println(err) + return nil, err + } + return l, nil +} + +func NewPacket(pkt gopacket.Packet) *netfilter.Packet { + return &netfilter.Packet{ + Packet: pkt, + UID: 666, + NetworkProtocol: netfilter.IPv4, + } +} + +func NewDummyConnection(src, dst net.IP) *Connection { + return &Connection{ + SrcIP: src, + DstIP: dst, + } +} + +// Test TCP parseDirection() +func TestParseTCPDirection(t *testing.T) { + srcIP := net.IP{192, 168, 1, 100} + dstIP := net.IP{1, 1, 1, 1} + c := NewDummyConnection(srcIP, dstIP) + // 47676:192.168.1.100 -> 1.1.1.1:23 + pkt := NewPacket(NewTCPPacket()) + c.Pkt = pkt + + // parseDirection extracts the src and dst port from a network packet. + if c.parseDirection("") == false { + t.Error("parseDirection() should not be false") + t.Fail() + } + if c.SrcPort != 47676 { + t.Error("parseDirection() SrcPort mismatch:", c) + t.Fail() + } + if c.DstPort != 23 { + t.Error("parseDirection() DstPort mismatch:", c) + t.Fail() + } + if c.Protocol != "tcp" { + t.Error("parseDirection() Protocol mismatch:", c) + t.Fail() + } +} + +// Test UDP parseDirection() +func TestParseUDPDirection(t *testing.T) { + srcIP := net.IP{192, 168, 1, 100} + dstIP := net.IP{1, 0, 0, 1} + c := NewDummyConnection(srcIP, dstIP) + // 29517:192.168.1.109 -> 1.0.0.1:53 + pkt := NewPacket(NewUDPPacket()) + c.Pkt = pkt + + // parseDirection extracts the src and dst port from a network packet. + if c.parseDirection("") == false { + t.Error("parseDirection() should not be false") + t.Fail() + } + if c.SrcPort != 29517 { + t.Error("parseDirection() SrcPort mismatch:", c) + t.Fail() + } + if c.DstPort != 53 { + t.Error("parseDirection() DstPort mismatch:", c) + t.Fail() + } + if c.Protocol != "udp" { + t.Error("parseDirection() Protocol mismatch:", c) + t.Fail() + } +} diff --git a/daemon/core/core.go b/daemon/core/core.go new file mode 100644 index 0000000..2c15ab5 --- /dev/null +++ b/daemon/core/core.go @@ -0,0 +1,78 @@ +package core + +import ( + "fmt" + "os" + "os/exec" + "os/user" + "path/filepath" + "strings" + "time" +) + +const ( + defaultTrimSet = "\r\n\t " +) + +// Trim remove trailing spaces from a string. +func Trim(s string) string { + return strings.Trim(s, defaultTrimSet) +} + +// Exec spawns a new process and reurns the output. +func Exec(executable string, args []string) (string, error) { + path, err := exec.LookPath(executable) + if err != nil { + return "", err + } + + raw, err := exec.Command(path, args...).CombinedOutput() + if err != nil { + return "", err + } + return Trim(string(raw)), nil +} + +// Exists checks if a path exists. +func Exists(path string) bool { + if _, err := os.Stat(path); os.IsNotExist(err) { + return false + } + return true +} + +// ExpandPath replaces '~' shorthand with the user's home directory. +func ExpandPath(path string) (string, error) { + // Check if path is empty + if path != "" { + if strings.HasPrefix(path, "~") { + usr, err := user.Current() + if err != nil { + return "", err + } + // Replace only the first occurrence of ~ + path = strings.Replace(path, "~", usr.HomeDir, 1) + } + return filepath.Abs(path) + } + return "", nil +} + +// IsAbsPath verifies if a path is absolute or not +func IsAbsPath(path string) bool { + return path[0] == 47 // 47 == '/' +} + +// GetFileModTime checks if a file has been modified. +func GetFileModTime(filepath string) (time.Time, error) { + fi, err := os.Stat(filepath) + if err != nil || fi.IsDir() { + return time.Now(), fmt.Errorf("GetFileModTime() Invalid file") + } + return fi.ModTime(), nil +} + +// ConcatStrings joins the provided strings. +func ConcatStrings(args ...string) string { + return strings.Join(args, "") +} diff --git a/daemon/core/ebpf.go b/daemon/core/ebpf.go new file mode 100644 index 0000000..c63a4e7 --- /dev/null +++ b/daemon/core/ebpf.go @@ -0,0 +1,49 @@ +package core + +import ( + "fmt" + + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/iovisor/gobpf/elf" +) + +// LoadEbpfModule loads the given eBPF module, from the given path if specified. +// Otherwise t'll try to load the module from several default paths. +func LoadEbpfModule(module, path string) (m *elf.Module, err error) { + var ( + modulesDir = "/opensnitchd/ebpf" + paths = []string{ + fmt.Sprint("/usr/local/lib", modulesDir), + fmt.Sprint("/usr/lib", modulesDir), + fmt.Sprint("/etc/opensnitchd"), // Deprecated: will be removed in future versions. + } + ) + + // if path has been specified, try to load the module from there. + if path != "" { + paths = []string{path} + } + + modulePath := "" + moduleError := fmt.Errorf(`Module not found (%s) in any of the paths. +You may need to install the corresponding package`, module) + + for _, p := range paths { + modulePath = fmt.Sprint(p, "/", module) + log.Debug("[eBPF] trying to load %s", modulePath) + if !Exists(modulePath) { + continue + } + m = elf.NewModule(modulePath) + + if m.Load(nil) == nil { + log.Info("[eBPF] module loaded: %s", modulePath) + return m, nil + } + moduleError = fmt.Errorf(` +unable to load eBPF module (%s). Your kernel version (%s) might not be compatible. +If this error persists, change process monitor method to 'proc'`, module, GetKernelVersion()) + } + + return m, moduleError +} diff --git a/daemon/core/gzip.go b/daemon/core/gzip.go new file mode 100644 index 0000000..34bb419 --- /dev/null +++ b/daemon/core/gzip.go @@ -0,0 +1,28 @@ +package core + +import ( + "compress/gzip" + "io/ioutil" + "os" +) + +// ReadGzipFile reads a gzip to text. +func ReadGzipFile(filename string) ([]byte, error) { + fd, err := os.Open(filename) + if err != nil { + return nil, err + } + defer fd.Close() + + gz, err := gzip.NewReader(fd) + if err != nil { + return nil, err + } + defer gz.Close() + + s, err := ioutil.ReadAll(gz) + if err != nil { + return nil, err + } + return s, nil +} diff --git a/daemon/core/system.go b/daemon/core/system.go new file mode 100644 index 0000000..f43637f --- /dev/null +++ b/daemon/core/system.go @@ -0,0 +1,198 @@ +package core + +import ( + "encoding/json" + "fmt" + "io" + "io/ioutil" + "regexp" + "strings" + + "github.com/evilsocket/opensnitch/daemon/log" +) + +var ( + // IPv6Enabled indicates if IPv6 protocol is enabled in the system + IPv6Enabled = Exists("/proc/sys/net/ipv6") +) + +// GetHostname returns the name of the host where the daemon is running. +func GetHostname() string { + hostname, _ := ioutil.ReadFile("/proc/sys/kernel/hostname") + return strings.Replace(string(hostname), "\n", "", -1) +} + +// GetKernelVersion returns the kernel version. +func GetKernelVersion() string { + version, _ := ioutil.ReadFile("/proc/sys/kernel/osrelease") + return strings.Replace(string(version), "\n", "", -1) +} + +// CheckSysRequirements checks system features we need to work properly +func CheckSysRequirements() { + type checksT struct { + RegExps []string + Reason string + } + type ReqsList struct { + Item string + Checks checksT + } + kVer := GetKernelVersion() + + log.Raw("\n\t%sChecking system requirements for kernel version %s%s\n", log.FG_WHITE+log.BG_LBLUE, kVer, log.RESET) + log.Raw("%s------------------------------------------------------------------------------%s\n\n", log.FG_WHITE+log.BG_LBLUE, log.RESET) + + confPaths := []string{ + fmt.Sprint("/boot/config-", kVer), + "/proc/config.gz", + // Fedora SilverBlue + fmt.Sprint("/usr/lib/modules/", kVer, "/config"), + } + + var fileContent []byte + var err error + for _, confFile := range confPaths { + if !Exists(confFile) { + err = fmt.Errorf("%s not found", confFile) + log.Debug(err.Error()) + continue + } + + if confFile[len(confFile)-2:] == "gz" { + fileContent, err = ReadGzipFile(confFile) + } else { + fileContent, err = ioutil.ReadFile(confFile) + } + if err == nil { + break + } + } + if err != nil { + fmt.Printf("\n\t%s kernel config not found (%s) in any of the expected paths.\n", log.Bold(log.Red("✘")), kVer) + fmt.Printf("\tPlease, open a new issue on github specifying your kernel and distro version (/etc/os-release).\n\n") + return + } + + // TODO: check loaded/configured modules (nfnetlink, nfnetlink_queue, xt_NFQUEUE, etc) + // Other items to check: + // CONFIG_NETFILTER_NETLINK + // CONFIG_NETFILTER_NETLINK_QUEUE + const reqsList = ` +[ +{ + "Item": "kprobes", + "Checks": { + "Regexps": [ + "CONFIG_KPROBES=y", + "CONFIG_KPROBES_ON_FTRACE=y", + "CONFIG_HAVE_KPROBES=y", + "CONFIG_HAVE_KPROBES_ON_FTRACE=y", + "CONFIG_KPROBE_EVENTS=y" + ], + "Reason": " - KPROBES not fully supported by this kernel." + } +}, +{ + "Item": "uprobes", + "Checks": { + "Regexps": [ + "CONFIG_UPROBES=y", + "CONFIG_UPROBE_EVENTS=y" + ], + "Reason": " * UPROBES not supported. Common error => cannot open uprobe_events: open /sys/kernel/debug/tracing/uprobe_events" + } +}, +{ + "Item": "ftrace", + "Checks": { + "Regexps": [ + "CONFIG_FTRACE=y" + ], + "Reason": " - CONFIG_TRACE=y not set. Common error => Error while loading kprobes: invalid argument." + } +}, +{ + "Item": "syscalls", + "Checks": { + "Regexps": [ + "CONFIG_HAVE_SYSCALL_TRACEPOINTS=y", + "CONFIG_FTRACE_SYSCALLS=y" + ], + "Reason": " - CONFIG_FTRACE_SYSCALLS or CONFIG_HAVE_SYSCALL_TRACEPOINTS not set. Common error => error enabling tracepoint tracepoint/syscalls/sys_enter_execve: cannot read tracepoint id" + } +}, +{ + "Item": "nfqueue", + "Checks": { + "Regexps": [ + "CONFIG_NETFILTER_NETLINK_QUEUE=[my]", + "CONFIG_NFT_QUEUE=[my]", + "CONFIG_NETFILTER_XT_TARGET_NFQUEUE=[my]" + ], + "Reason": " * NFQUEUE netfilter extensions not supported by this kernel (CONFIG_NETFILTER_NETLINK_QUEUE, CONFIG_NFT_QUEUE, CONFIG_NETFILTER_XT_TARGET_NFQUEUE)." + } +}, +{ + "Item": "netlink", + "Checks": { + "Regexps": [ + "CONFIG_NETFILTER_NETLINK=[my]", + "CONFIG_NETFILTER_NETLINK_QUEUE=[my]", + "CONFIG_NETFILTER_NETLINK_ACCT=[my]" + ], + "Reason": " * NETLINK extensions not supported by this kernel (CONFIG_NETFILTER_NETLINK, CONFIG_NETFILTER_NETLINK_QUEUE, CONFIG_NETFILTER_NETLINK_ACCT)." + } +}, +{ + "Item": "net diagnostics", + "Checks": { + "Regexps": [ + "CONFIG_INET_DIAG=[my]", + "CONFIG_INET_TCP_DIAG=[my]", + "CONFIG_INET_UDP_DIAG=[my]", + "CONFIG_INET_DIAG_DESTROY=[my]" + ], + "Reason": " * One or more socket monitoring interfaces are not enabled (CONFIG_INET_DIAG, CONFIG_INET_TCP_DIAG, CONFIG_INET_UDP_DIAG, CONFIG_DIAG_DESTROY (Reject feature))." + } +} +] +` + + reqsFullfiled := true + dec := json.NewDecoder(strings.NewReader(reqsList)) + for { + var reqs []ReqsList + if err := dec.Decode(&reqs); err == io.EOF { + break + } else if err != nil { + log.Error("%s", err) + break + } + for _, req := range reqs { + checkOk := true + for _, trex := range req.Checks.RegExps { + fmt.Printf("\tChecking => %s\n", trex) + re, err := regexp.Compile(trex) + if err != nil { + fmt.Printf("\t%s %s\n", log.Bold(log.Red("Invalid regexp =>")), log.Red(trex)) + continue + } + if re.Find(fileContent) == nil { + fmt.Printf("\t%s\n", log.Red(req.Checks.Reason)) + checkOk = false + } + } + if checkOk { + fmt.Printf("\n\t* %s\t %s\n", log.Bold(log.Green(req.Item)), log.Bold(log.Green("✔"))) + } else { + reqsFullfiled = false + fmt.Printf("\n\t* %s\t %s\n", log.Bold(log.Red(req.Item)), log.Bold(log.Red("✘"))) + } + fmt.Println() + } + } + if !reqsFullfiled { + log.Raw("\n%sWARNING:%s Your kernel doesn't support some of the features OpenSnitch needs:\nRead more: https://github.com/evilsocket/opensnitch/issues/774\n", log.FG_WHITE+log.BG_YELLOW, log.RESET) + } +} diff --git a/daemon/core/version.go b/daemon/core/version.go new file mode 100644 index 0000000..561b1f6 --- /dev/null +++ b/daemon/core/version.go @@ -0,0 +1,9 @@ +package core + +// version related consts +const ( + Name = "opensnitch-daemon" + Version = "1.6.9" + Author = "Simone 'evilsocket' Margaritelli" + Website = "https://github.com/evilsocket/opensnitch" +) diff --git a/daemon/data/rules/000-allow-localhost.json b/daemon/data/rules/000-allow-localhost.json new file mode 100644 index 0000000..a8320b2 --- /dev/null +++ b/daemon/data/rules/000-allow-localhost.json @@ -0,0 +1,17 @@ +{ + "created": "2023-07-05T10:46:47.904024069+01:00", + "updated": "2023-07-05T10:46:47.921828104+01:00", + "name": "000-allow-localhost", + "description": "Allow connections to localhost. See this link for more information:\nhttps://github.com/evilsocket/opensnitch/wiki/Rules#localhost-connections", + "enabled": true, + "precedence": true, + "action": "allow", + "duration": "always", + "operator": { + "type": "regexp", + "operand": "dest.ip", + "sensitive": false, + "data": "^(127\\.0\\.0\\.1|::1)$", + "list": [] + } +} diff --git a/daemon/default-config.json b/daemon/default-config.json new file mode 100644 index 0000000..302014a --- /dev/null +++ b/daemon/default-config.json @@ -0,0 +1,27 @@ +{ + "Server": + { + "Address":"unix:///tmp/osui.sock", + "LogFile":"/var/log/opensnitchd.log" + }, + "DefaultAction": "allow", + "DefaultDuration": "once", + "InterceptUnknown": false, + "ProcMonitorMethod": "ebpf", + "LogLevel": 2, + "LogUTC": true, + "LogMicro": false, + "Firewall": "nftables", + "Rules": { + "Path": "/etc/opensnitchd/rules/" + }, + "Stats": { + "MaxEvents": 150, + "MaxStats": 25, + "Workers": 6 + }, + "Internal": { + "GCPercent": 100, + "FlushConnsOnStart": true + } +} diff --git a/daemon/dns/ebpfhook.go b/daemon/dns/ebpfhook.go new file mode 100644 index 0000000..83a37df --- /dev/null +++ b/daemon/dns/ebpfhook.go @@ -0,0 +1,212 @@ +package dns + +import ( + "bytes" + "debug/elf" + "encoding/binary" + "errors" + "fmt" + "net" + "os" + "os/signal" + "strings" + "syscall" + "time" + + "github.com/evilsocket/opensnitch/daemon/core" + "github.com/evilsocket/opensnitch/daemon/log" + bpf "github.com/iovisor/gobpf/elf" +) + +/* +#cgo LDFLAGS: -ldl + +#define _GNU_SOURCE +#include <stdio.h> +#include <stdlib.h> +#include <link.h> +#include <dlfcn.h> +#include <string.h> + +char* find_libc() { + void *handle; + struct link_map * map; + + handle = dlopen(NULL, RTLD_NOW); + if (handle == NULL) { + fprintf(stderr, "EBPF-DNS dlopen() failed: %s\n", dlerror()); + return NULL; + } + + + if (dlinfo(handle, RTLD_DI_LINKMAP, &map) == -1) { + fprintf(stderr, "EBPF-DNS: dlinfo failed: %s\n", dlerror()); + return NULL; + } + + while(1){ + if(map == NULL){ + break; + } + + if(strstr(map->l_name, "libc.so")){ + fprintf(stderr,"found %s\n", map->l_name); + return map->l_name; + } + map = map->l_next; + } + return NULL; +} + + +*/ +import "C" + +type nameLookupEvent struct { + AddrType uint32 + IP [16]uint8 + Host [252]byte +} + +func findLibc() (string, error) { + ret := C.find_libc() + + if ret == nil { + return "", errors.New("Could not find path to libc.so") + } + str := C.GoString(ret) + + return str, nil +} + +// Iterates over all symbols in an elf file and returns the offset matching the provided symbol name. +func lookupSymbol(elffile *elf.File, symbolName string) (uint64, error) { + symbols, err := elffile.DynamicSymbols() + if err != nil { + return 0, err + } + for _, symb := range symbols { + if symb.Name == symbolName { + return symb.Value, nil + } + } + return 0, fmt.Errorf("Symbol: '%s' not found", symbolName) +} + +// ListenerEbpf starts listening for DNS events. +func ListenerEbpf(ebpfModPath string) error { + m, err := core.LoadEbpfModule("opensnitch-dns.o", ebpfModPath) + if err != nil { + log.Error("[eBPF DNS]: %s", err) + return err + } + defer m.Close() + + // libbcc resolves the offsets for us. without bcc the offset for uprobes must parsed from the elf files + // some how 0 must be replaced with the offset of getaddrinfo bcc does this using bcc_resolve_symname + + // Attaching to uprobe using perf open might be a better aproach requires https://github.com/iovisor/gobpf/pull/277 + libcFile, err := findLibc() + + if err != nil { + log.Error("EBPF-DNS: Failed to find libc.so: %v", err) + return err + } + + libcElf, err := elf.Open(libcFile) + if err != nil { + log.Error("EBPF-DNS: Failed to open %s: %v", libcFile, err) + return err + } + probesAttached := 0 + for uprobe := range m.IterUprobes() { + probeFunction := strings.Replace(uprobe.Name, "uretprobe/", "", 1) + probeFunction = strings.Replace(probeFunction, "uprobe/", "", 1) + offset, err := lookupSymbol(libcElf, probeFunction) + if err != nil { + log.Warning("EBPF-DNS: Failed to find symbol for uprobe %s (offset: %d): %s\n", uprobe.Name, offset, err) + continue + } + err = bpf.AttachUprobe(uprobe, libcFile, offset) + if err != nil { + log.Warning("EBPF-DNS: Failed to attach uprobe %s : %s, (%s, %d)\n", uprobe.Name, err, libcFile, offset) + continue + } + probesAttached++ + } + + if probesAttached == 0 { + log.Warning("EBPF-DNS: Failed to find symbols for uprobes.") + return errors.New("Failed to find symbols for uprobes") + } + + // Reading Events + channel := make(chan []byte) + //log.Warning("EBPF-DNS: %+v\n", m) + perfMap, err := bpf.InitPerfMap(m, "events", channel, nil) + if err != nil { + log.Error("EBPF-DNS: Failed to init perf map: %s\n", err) + return err + } + sig := make(chan os.Signal, 1) + exitChannel := make(chan bool) + signal.Notify(sig, + syscall.SIGHUP, + syscall.SIGINT, + syscall.SIGTERM, + syscall.SIGKILL, + syscall.SIGQUIT) + + for i := 0; i < 5; i++ { + go spawnDNSWorker(i, channel, exitChannel) + } + + perfMap.PollStart() + <-sig + log.Info("EBPF-DNS: Received signal: terminating ebpf dns hook.") + perfMap.PollStop() + for i := 0; i < 5; i++ { + exitChannel <- true + } + return nil +} + +func spawnDNSWorker(id int, channel chan []byte, exitChannel chan bool) { + + log.Debug("dns worker initialized #%d", id) + var event nameLookupEvent + for { + select { + + case <-time.After(1 * time.Millisecond): + continue + case <-exitChannel: + goto Exit + default: + data := <-channel + if len(data) > 0 { + log.Debug("(%d) EBPF-DNS: LookupEvent %d %x %x %x", id, len(data), data[:4], data[4:20], data[20:]) + } + err := binary.Read(bytes.NewBuffer(data), binary.LittleEndian, &event) + if err != nil { + log.Warning("(%d) EBPF-DNS: Failed to decode ebpf nameLookupEvent: %s\n", id, err) + continue + } + // Convert C string (null-terminated) to Go string + host := string(event.Host[:bytes.IndexByte(event.Host[:], 0)]) + var ip net.IP + // 2 -> AF_INET (ipv4) + if event.AddrType == 2 { + ip = net.IP(event.IP[:4]) + } else { + ip = net.IP(event.IP[:]) + } + + log.Debug("(%d) EBPF-DNS: Tracking Resolved Message: %s -> %s\n", id, host, ip.String()) + Track(ip.String(), host) + } + } + +Exit: + log.Debug("DNS worker #%d closed", id) +} diff --git a/daemon/dns/parse.go b/daemon/dns/parse.go new file mode 100644 index 0000000..971eafe --- /dev/null +++ b/daemon/dns/parse.go @@ -0,0 +1,21 @@ +package dns + +import ( + "github.com/evilsocket/opensnitch/daemon/netfilter" + "github.com/google/gopacket/layers" +) + +// GetQuestions retrieves the domain names a process is trying to resolve. +func GetQuestions(nfp *netfilter.Packet) (questions []string) { + dnsLayer := nfp.Packet.Layer(layers.LayerTypeDNS) + if dnsLayer == nil { + return questions + } + + dns, _ := dnsLayer.(*layers.DNS) + for _, dnsQuestion := range dns.Questions { + questions = append(questions, string(dnsQuestion.Name)) + } + + return questions +} diff --git a/daemon/dns/systemd/monitor.go b/daemon/dns/systemd/monitor.go new file mode 100644 index 0000000..0b51e2b --- /dev/null +++ b/daemon/dns/systemd/monitor.go @@ -0,0 +1,237 @@ +// Package systemd defines several utilities to interact with systemd. +// +// ResolvedMonitor: +// * To debug systemd-resolved queries and inspect the protocol: +// - resolvectl monitor +// * Resources: +// - https://github.com/systemd/systemd/blob/main/src/resolve/resolvectl.c +// - The protocol used to send and receive data is varlink: +// https://github.com/varlink/go +// https://github.com/systemd/systemd/blob/main/src/resolve/resolved-varlink.c +// - https://systemd.io/RESOLVED-VPNS/ +package systemd + +import ( + "context" + "errors" + "fmt" + "sync" + "time" + + "github.com/evilsocket/opensnitch/daemon/core" + "github.com/evilsocket/opensnitch/daemon/log" + + "github.com/varlink/go/varlink" +) + +// whenever there's a new DNS response, this callback will be invoked. +// the second parameter is a MonitorResponse struct that will be filled with +// data. +type resolvedCallback func(context.Context, interface{}) (uint64, error) + +const ( + // SuccessState is the string returned by systemd-resolved when a DNS query is successful. + // Other states: https://github.com/systemd/systemd/blob/main/src/resolve/resolved-dns-transaction.c#L3608 + SuccessState = "success" + + socketPath = "/run/systemd/resolve/io.systemd.Resolve.Monitor" + resolvedSubscribeMethod = "io.systemd.Resolve.Monitor.SubscribeQueryResults" + + // DNSTypeA A + DNSTypeA = 1 + // DNSTypeAAAA AAAA + DNSTypeAAAA = 28 + // DNSTypeCNAME cname + DNSTypeCNAME = 5 +) + +// QuestionMonitorResponse represents a DNS query +// "question": [{"class": 1, "type": 28,"name": "images.site.com"}], +type QuestionMonitorResponse struct { + Name string `json:"name"` + Class int `json:"class"` + Type int `json:"type"` +} + +// KeyType holds question that generated the answer +/*answer: [{ + "rr": { + "key": { + "class": 1, + "type": 28, + "name": "images.site.com" + }, + "address": [100, 13, 45, 111] + }, + "raw": "DFJFKE343443EFKEREKET=", + "ifindex": 3 +}]*/ +type KeyType struct { + Name string `json:"name"` + Class int `json:"class"` + Type int `json:"type"` +} + +// RRType represents a DNS answer +// if the response is a CNAME, Address will be nil, and Name a domain name. +type RRType struct { + Key QuestionMonitorResponse `json:"key"` + Address []byte `json:"address"` + Name string `json:"name"` +} + +// AnswerMonitorResponse represents the DNS answer of a DNS query. +type AnswerMonitorResponse struct { + RR RRType `json:"rr"` + Raw string `json:"raw"` + Ifindex int `json:"ifindex"` +} + +// MonitorResponse represents the systemd-resolved protocol message +// sent over the wire, that holds the answer to a DNS query. +type MonitorResponse struct { + State string `json:"state"` + Question []QuestionMonitorResponse `json:"question"` + // CollectedQuestions + // "collectedQuestions":[{"class":1,"type":1,"name":"translate.google.com"}] + Answer []AnswerMonitorResponse `json:"answer"` + Continues bool `json:"continues"` +} + +// ResolvedMonitor represents a systemd-resolved monitor +type ResolvedMonitor struct { + mu *sync.RWMutex + Ctx context.Context + Cancel context.CancelFunc + + // connection with the systemd-resolved unix socket: + // /run/systemd/resolve/io.systemd.Resolve.Monitor + Conn *varlink.Connection + + // channel where all the DNS respones will be sent + ChanResponse chan *MonitorResponse + + // error channel to signal any problem + ChanConnError chan error + + // callback that is emited when systemd-resolved resolves a domain name. + receiverCb resolvedCallback + + connected bool +} + +// NewResolvedMonitor returns a new ResolvedMonitor object. +// With this object you can passively read DNS answers. +func NewResolvedMonitor() (*ResolvedMonitor, error) { + if core.Exists(socketPath) == false { + return nil, fmt.Errorf("%s doesn't exist", socketPath) + } + ctx, cancel := context.WithCancel(context.Background()) + + return &ResolvedMonitor{ + mu: &sync.RWMutex{}, + Ctx: ctx, + Cancel: cancel, + ChanResponse: make(chan *MonitorResponse), + ChanConnError: make(chan error), + }, nil +} + +// Connect opens a unix socket with systemd-resolved +func (r *ResolvedMonitor) Connect() (*varlink.Connection, error) { + r.mu.Lock() + defer r.mu.Unlock() + + var err error + r.Conn, err = varlink.NewConnection(r.Ctx, fmt.Sprintf("unix://%s", socketPath)) + if err != nil { + return nil, err + } + + r.connected = true + go r.connPoller() + return r.Conn, nil +} + +// if we're connected to the unix socket, check every few seconds if we're still +// connected, and if not, reconnect, to survive to systemd-resolved restarts. +func (r *ResolvedMonitor) connPoller() { + for { + select { + case <-time.After(5 * time.Second): + if r.isConnected() { + continue + } + log.Debug("ResolvedMonitor not connected") + if _, err := r.Connect(); err == nil { + r.Subscribe() + } + goto Exit + } + } +Exit: + log.Debug("ResolvedMonitor connection poller exit.") +} + +// Subscribe sends the instruction to systemd-resolved to start monitoring +// DNS answers. +func (r *ResolvedMonitor) Subscribe() error { + if r.isConnected() == false { + return errors.New("Not connected") + } + var err error + type emptyT struct{} + empty := &emptyT{} + r.receiverCb, err = r.Conn.Send(r.Ctx, resolvedSubscribeMethod, empty, varlink.Continues|varlink.More) + if err != nil { + return err + } + go r.monitor(r.Ctx, r.ChanResponse, r.ChanConnError, r.receiverCb) + + return nil +} + +// monitor will listen for DNS answers from systemd-resolved. +func (r *ResolvedMonitor) monitor(ctx context.Context, chanResponse chan *MonitorResponse, chanConnError chan error, callback resolvedCallback) { + for { + m := &MonitorResponse{} + continues, err := callback(ctx, m) + if err != nil { + chanConnError <- err + goto Exit + } + if continues != varlink.Continues { + goto Exit + } + log.Debug("ResolvedMonitor >> new response: %#v", m) + chanResponse <- m + } + +Exit: + r.mu.Lock() + r.connected = false + r.mu.Unlock() + log.Debug("ResolvedMonitor.monitor() exit.") +} + +// GetDNSResponses returns a channel that you can use to read responses. +func (r *ResolvedMonitor) GetDNSResponses() chan *MonitorResponse { + return r.ChanResponse +} + +// Exit returns a channel to listen for connection errors. +func (r *ResolvedMonitor) Exit() chan error { + return r.ChanConnError +} + +// Close closes the unix socket with systemd-resolved +func (r *ResolvedMonitor) Close() { + r.ChanConnError <- nil + r.Cancel() +} + +func (r *ResolvedMonitor) isConnected() bool { + r.mu.RLock() + defer r.mu.RUnlock() + return r.connected +} diff --git a/daemon/dns/track.go b/daemon/dns/track.go new file mode 100644 index 0000000..bf3b723 --- /dev/null +++ b/daemon/dns/track.go @@ -0,0 +1,102 @@ +package dns + +import ( + "net" + "sync" + + "github.com/evilsocket/opensnitch/daemon/log" + + "github.com/google/gopacket" + "github.com/google/gopacket/layers" +) + +var ( + responses = make(map[string]string, 0) + lock = sync.RWMutex{} +) + +// TrackAnswers obtains the resolved domains of a DNS query. +// If the packet is UDP DNS, the domain names are added to the list of resolved domains. +func TrackAnswers(packet gopacket.Packet) bool { + udpLayer := packet.Layer(layers.LayerTypeUDP) + if udpLayer == nil { + return false + } + + udp, ok := udpLayer.(*layers.UDP) + if ok == false || udp == nil { + return false + } + if udp.SrcPort != 53 { + return false + } + + dnsLayer := packet.Layer(layers.LayerTypeDNS) + if dnsLayer == nil { + return false + } + + dnsAns, ok := dnsLayer.(*layers.DNS) + if ok == false || dnsAns == nil { + return false + } + + for _, ans := range dnsAns.Answers { + if ans.Name != nil { + if ans.IP != nil { + Track(ans.IP.String(), string(ans.Name)) + } else if ans.CNAME != nil { + Track(string(ans.CNAME), string(ans.Name)) + } + } + } + + return true +} + +// Track adds a resolved domain to the list. +func Track(resolved string, hostname string) { + lock.Lock() + defer lock.Unlock() + + if len(resolved) > 3 && resolved[0:4] == "127." { + return + } + if resolved == "::1" || resolved == hostname { + return + } + responses[resolved] = hostname + + log.Debug("New DNS record: %s -> %s", resolved, hostname) +} + +// Host returns if a resolved domain is in the list. +func Host(resolved string) (host string, found bool) { + lock.RLock() + defer lock.RUnlock() + + host, found = responses[resolved] + return +} + +// HostOr checks if an IP has a domain name already resolved. +// If the domain is in the list it's returned, otherwise the IP will be returned. +func HostOr(ip net.IP, or string) string { + if host, found := Host(ip.String()); found == true { + // host might have been CNAME; go back until we reach the "root" + seen := make(map[string]bool) // prevent possibility of loops + for { + orig, had := Host(host) + if seen[orig] { + break + } + if !had { + break + } + seen[orig] = true + host = orig + } + return host + } + return or +} diff --git a/daemon/firewall/common/common.go b/daemon/firewall/common/common.go new file mode 100644 index 0000000..67e3aff --- /dev/null +++ b/daemon/firewall/common/common.go @@ -0,0 +1,170 @@ +package common + +import ( + "sync" + "time" + + "github.com/evilsocket/opensnitch/daemon/log" +) + +// default arguments for various functions +var ( + EnableRule = true + DoLogErrors = true + ForcedDelRules = true + ReloadRules = true + RestoreChains = true + BackupChains = true + ReloadConf = true +) + +type ( + callback func() + callbackBool func() bool + + // Common holds common fields and functionality of both firewalls, + // iptables and nftables. + Common struct { + RulesChecker *time.Ticker + ErrChan chan string + QueueNum uint16 + stopChecker chan bool + Running bool + Intercepting bool + FwEnabled bool + sync.RWMutex + } +) + +// ErrorsChan returns the channel where the errors are sent to. +func (c *Common) ErrorsChan() <-chan string { + return c.ErrChan +} + +// ErrChanEmpty checks if the errors channel is empty. +func (c *Common) ErrChanEmpty() bool { + return len(c.ErrChan) == 0 +} + +// SendError sends an error to the channel of errors. +func (c *Common) SendError(err string) { + log.Warning("%s", err) + + if len(c.ErrChan) >= cap(c.ErrChan) { + log.Debug("fw errors channel full, emptying errChan") + for e := range c.ErrChan { + log.Warning("%s", e) + if c.ErrChanEmpty() { + break + } + } + return + } + select { + case c.ErrChan <- err: + case <-time.After(100 * time.Millisecond): + log.Warning("SendError() channel locked? REVIEW") + } +} + +// SetQueueNum sets the queue number used by the firewall. +// It's the queue where all intercepted connections will be sent. +func (c *Common) SetQueueNum(qNum *int) { + c.Lock() + defer c.Unlock() + + if qNum != nil { + c.QueueNum = uint16(*qNum) + } + +} + +// IsRunning returns if the firewall is running or not. +func (c *Common) IsRunning() bool { + c.RLock() + defer c.RUnlock() + + return c != nil && c.Running +} + +// IsFirewallEnabled returns if the firewall is running or not. +func (c *Common) IsFirewallEnabled() bool { + c.RLock() + defer c.RUnlock() + + return c != nil && c.FwEnabled +} + +// IsIntercepting returns if the firewall is running or not. +func (c *Common) IsIntercepting() bool { + c.RLock() + defer c.RUnlock() + + return c != nil && c.Intercepting +} + +// NewRulesChecker starts monitoring interception rules. +// We expect to have 2 rules loaded: one to intercept DNS responses and another one +// to intercept network traffic. +func (c *Common) NewRulesChecker(areRulesLoaded callbackBool, reloadRules callback) { + c.Lock() + defer c.Unlock() + + if c.RulesChecker != nil { + c.RulesChecker.Stop() + select { + case c.stopChecker <- true: + case <-time.After(5 * time.Millisecond): + log.Error("NewRulesChecker: timed out stopping monitor rules") + } + } + c.stopChecker = make(chan bool, 1) + c.RulesChecker = time.NewTicker(time.Second * 10) + + go startCheckingRules(c.stopChecker, c.RulesChecker, areRulesLoaded, reloadRules) +} + +// StartCheckingRules monitors if our rules are loaded. +// If the rules to intercept traffic are not loaded, we'll try to insert them again. +func startCheckingRules(exitChan <-chan bool, rulesChecker *time.Ticker, areRulesLoaded callbackBool, reloadRules callback) { + for { + select { + case <-exitChan: + goto Exit + case _, active := <-rulesChecker.C: + if !active { + goto Exit + } + + if areRulesLoaded() == false { + reloadRules() + } + } + } + +Exit: + log.Info("exit checking firewall rules") +} + +// StopCheckingRules stops checking if firewall rules are loaded. +func (c *Common) StopCheckingRules() { + c.Lock() + defer c.Unlock() + + if c.RulesChecker != nil { + select { + case c.stopChecker <- true: + close(c.stopChecker) + case <-time.After(5 * time.Millisecond): + // We should not arrive here + log.Error("StopCheckingRules: timed out stopping monitor rules") + } + + c.RulesChecker.Stop() + c.RulesChecker = nil + } +} + +func (c *Common) reloadCallback(callback func()) { + callback() +} diff --git a/daemon/firewall/config/config.go b/daemon/firewall/config/config.go new file mode 100644 index 0000000..43c6c9e --- /dev/null +++ b/daemon/firewall/config/config.go @@ -0,0 +1,254 @@ +// Package config provides functionality to load and monitor the system +// firewall rules. +// It's inherited by the different firewall packages (iptables, nftables). +// +// The firewall rules defined by the user are reloaded in these cases: +// - When the file system-fw.json changes. +// - When the firewall rules are not present when listing them. +package config + +import ( + "encoding/json" + "io/ioutil" + "os" + "sync" + + "github.com/evilsocket/opensnitch/daemon/firewall/common" + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/fsnotify/fsnotify" +) + +// ExprValues holds the statements' options: +// "Name": "ct", +// "Values": [ +// { +// "Key": "state", +// "Value": "established" +// }, +// { +// "Key": "state", +// "Value": "related" +// }] +type ExprValues struct { + Key string + Value string +} + +// ExprStatement holds the definition of matches to use against connections. +//{ +// "Op": "!=", +// "Name": "tcp", +// "Values": [ +// { +// "Key": "dport", +// "Value": "443" +// } +// ] +//} +type ExprStatement struct { + Op string // ==, !=, ... Only one per expression set. + Name string // tcp, udp, ct, daddr, log, ... + Values []*ExprValues // dport 8000 +} + +// Expressions holds the array of expressions that create the rules +type Expressions struct { + Statement *ExprStatement +} + +// FwRule holds the fields of a rule +type FwRule struct { + *sync.RWMutex + // we need to keep old fields in the struct. Otherwise when receiving a conf from the GUI, the legacy rules would be deleted. + Chain string // TODO: deprecated, remove + Table string // TODO: deprecated, remove + Parameters string // TODO: deprecated, remove + UUID string + Description string + Target string + TargetParameters string + Expressions []*Expressions + Position uint64 `json:",string"` + Enabled bool +} + +// FwChain holds the information that defines a firewall chain. +// It also contains the firewall table definition that it belongs to. +type FwChain struct { + // table fields + Table string + Family string + // chain fields + Name string + Description string + Priority string + Type string + Hook string + Policy string + Rules []*FwRule +} + +// IsInvalid checks if the chain has been correctly configured. +func (fc *FwChain) IsInvalid() bool { + return fc.Name == "" || fc.Family == "" || fc.Table == "" +} + +type rulesList struct { + Rule *FwRule +} + +type chainsList struct { + Rule *FwRule // TODO: deprecated, remove + Chains []*FwChain +} + +// SystemConfig holds the list of rules to be added to the system +type SystemConfig struct { + SystemRules []*chainsList + sync.RWMutex + Version uint32 + Enabled bool +} + +// Config holds the functionality to re/load the firewall configuration from disk. +// This is the configuration to manage the system firewall (iptables, nftables). +type Config struct { + watcher *fsnotify.Watcher + monitorExitChan chan bool + // preload will be called after daemon startup, whilst reload when a modification is performed. + preloadCallback func() + // reloadCallback is called after the configuration is written. + reloadCallback func() + file string + SysConfig SystemConfig + + sync.Mutex +} + +// NewSystemFwConfig initializes config fields +func (c *Config) NewSystemFwConfig(preLoadCb, reLoadCb func()) (*Config, error) { + var err error + watcher, err := fsnotify.NewWatcher() + if err != nil { + log.Warning("Error creating firewall config watcher: %s", err) + return nil, err + } + + c.Lock() + defer c.Unlock() + + c.file = "/etc/opensnitchd/system-fw.json" + c.monitorExitChan = make(chan bool, 1) + c.preloadCallback = preLoadCb + c.reloadCallback = reLoadCb + c.watcher = watcher + return c, nil +} + +func (c *Config) SetFile(file string) { + c.file = file +} + +// LoadDiskConfiguration reads and loads the firewall configuration from disk +func (c *Config) LoadDiskConfiguration(reload bool) error { + c.Lock() + defer c.Unlock() + + raw, err := ioutil.ReadFile(c.file) + if err != nil { + log.Error("Error reading firewall configuration from disk %s: %s", c.file, err) + return err + } + + if err = c.loadConfiguration(raw); err != nil { + return err + } + // we need to monitor the configuration file for changes, regardless if it's + // malformed or not. + c.watcher.Remove(c.file) + if err := c.watcher.Add(c.file); err != nil { + log.Error("Could not watch firewall configuration: %s", err) + return err + } + + if reload { + c.reloadCallback() + return nil + } + + go c.monitorConfigWorker() + + return nil +} + +// loadConfigutation reads the system firewall rules from disk. +// Then the rules are added based on the configuration defined. +func (c *Config) loadConfiguration(rawConfig []byte) error { + c.SysConfig.Lock() + defer c.SysConfig.Unlock() + + // delete old system rules, that may be different from the new ones + c.preloadCallback() + + if err := json.Unmarshal(rawConfig, &c.SysConfig); err != nil { + // we only log the parser error, giving the user a chance to write a valid config + log.Error("Error parsing firewall configuration %s: %s", c.file, err) + return err + } + log.Info("fw configuration loaded") + + return nil +} + +// SaveConfiguration saves configuration to disk. +// This event dispatches a reload of the configuration. +func (c *Config) SaveConfiguration(rawConfig string) error { + conf, err := json.MarshalIndent([]byte(rawConfig), " ", " ") + if err != nil { + log.Error("saving json firewall configuration: %s %s", err, conf) + return err + } + + if err = os.Chmod(c.file, 0600); err != nil { + log.Warning("unable to set system-fw.json permissions: %s", err) + } + if err = ioutil.WriteFile(c.file, []byte(rawConfig), 0600); err != nil { + log.Error("writing firewall configuration to disk: %s", err) + return err + } + return nil +} + +// StopConfigWatcher stops the configuration watcher and stops the subroutine. +func (c *Config) StopConfigWatcher() { + c.Lock() + defer c.Unlock() + + if c.monitorExitChan != nil { + c.monitorExitChan <- true + close(c.monitorExitChan) + } + + if c.watcher != nil { + c.watcher.Remove(c.file) + c.watcher.Close() + } +} + +func (c *Config) monitorConfigWorker() { + for { + select { + case <-c.monitorExitChan: + goto Exit + case event := <-c.watcher.Events: + if (event.Op&fsnotify.Write == fsnotify.Write) || (event.Op&fsnotify.Remove == fsnotify.Remove) { + c.LoadDiskConfiguration(common.ReloadConf) + } + } + } +Exit: + log.Debug("stop monitoring firewall config file") + c.Lock() + c.monitorExitChan = nil + c.Unlock() +} diff --git a/daemon/firewall/iptables/iptables.go b/daemon/firewall/iptables/iptables.go new file mode 100644 index 0000000..513ecd7 --- /dev/null +++ b/daemon/firewall/iptables/iptables.go @@ -0,0 +1,198 @@ +package iptables + +import ( + "bytes" + "encoding/json" + "os/exec" + "regexp" + "strings" + "sync" + + "github.com/evilsocket/opensnitch/daemon/firewall/common" + "github.com/evilsocket/opensnitch/daemon/firewall/config" + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/evilsocket/opensnitch/daemon/ui/protocol" + "github.com/golang/protobuf/jsonpb" +) + +// Action is the modifier we apply to a rule. +type Action string + +const ( + // Name is the name that identifies this firewall + Name = "iptables" + // SystemRulePrefix prefix added to each system rule + SystemRulePrefix = "opensnitch-filter" +) + +// Actions we apply to the firewall. +const ( + ADD = Action("-A") + INSERT = Action("-I") + DELETE = Action("-D") + FLUSH = Action("-F") + NEWCHAIN = Action("-N") + DELCHAIN = Action("-X") + POLICY = Action("-P") + + DROP = Action("DROP") + ACCEPT = Action("ACCEPT") +) + +// SystemRule blabla +type SystemRule struct { + Rule *config.FwRule + Table string + Chain string +} + +// SystemChains keeps track of the fw rules that have been added to the system. +type SystemChains struct { + Rules map[string]*SystemRule + sync.RWMutex +} + +// Iptables struct holds the fields of the iptables fw +type Iptables struct { + regexRulesQuery *regexp.Regexp + regexSystemRulesQuery *regexp.Regexp + bin string + bin6 string + chains SystemChains + common.Common + config.Config + sync.Mutex +} + +// Fw initializes a new Iptables object +func Fw() (*Iptables, error) { + if err := IsAvailable(); err != nil { + return nil, err + } + + reRulesQuery, _ := regexp.Compile(`NFQUEUE.*ctstate NEW,RELATED.*NFQUEUE num.*bypass`) + reSystemRulesQuery, _ := regexp.Compile(SystemRulePrefix + ".*") + + ipt := &Iptables{ + bin: "iptables", + bin6: "ip6tables", + regexRulesQuery: reRulesQuery, + regexSystemRulesQuery: reSystemRulesQuery, + chains: SystemChains{ + Rules: make(map[string]*SystemRule), + }, + } + return ipt, nil +} + +// Name returns the firewall name +func (ipt *Iptables) Name() string { + return Name +} + +// Init inserts the firewall rules and starts monitoring for firewall +// changes. +func (ipt *Iptables) Init(qNum *int) { + if ipt.IsRunning() { + return + } + ipt.SetQueueNum(qNum) + ipt.ErrChan = make(chan string, 100) + + // In order to clean up any existing firewall rule before start, + // we need to load the fw configuration first to know what rules + // were configured. + ipt.NewSystemFwConfig(ipt.preloadConfCallback, ipt.reloadRulesCallback) + ipt.LoadDiskConfiguration(!common.ReloadConf) + + // start from a clean state + ipt.CleanRules(false) + ipt.EnableInterception() + ipt.AddSystemRules(!common.ReloadRules, common.BackupChains) + + ipt.Running = true +} + +// Stop deletes the firewall rules, allowing network traffic. +func (ipt *Iptables) Stop() { + if ipt.Running == false { + return + } + ipt.StopConfigWatcher() + ipt.StopCheckingRules() + ipt.CleanRules(log.GetLogLevel() == log.DEBUG) + + ipt.Running = false +} + +// IsAvailable checks if iptables is installed in the system. +// If it's not, we'll default to nftables. +func IsAvailable() error { + _, err := exec.Command("iptables", []string{"-V"}...).CombinedOutput() + if err != nil { + return err + } + return nil +} + +// EnableInterception adds fw rules to intercept connections. +func (ipt *Iptables) EnableInterception() { + if err4, err6 := ipt.QueueConnections(common.EnableRule, true); err4 != nil || err6 != nil { + log.Fatal("Error while running conntrack firewall rule: %s %s", err4, err6) + } else if err4, err6 = ipt.QueueDNSResponses(common.EnableRule, true); err4 != nil || err6 != nil { + log.Error("Error while running DNS firewall rule: %s %s", err4, err6) + } + // start monitoring firewall rules to intercept network traffic + ipt.NewRulesChecker(ipt.AreRulesLoaded, ipt.reloadRulesCallback) +} + +// DisableInterception removes firewall rules to intercept outbound connections. +func (ipt *Iptables) DisableInterception(logErrors bool) { + ipt.StopCheckingRules() + ipt.QueueDNSResponses(!common.EnableRule, logErrors) + ipt.QueueConnections(!common.EnableRule, logErrors) +} + +// CleanRules deletes the rules we added. +func (ipt *Iptables) CleanRules(logErrors bool) { + ipt.DisableInterception(logErrors) + ipt.DeleteSystemRules(common.ForcedDelRules, common.BackupChains, logErrors) +} + +// Serialize converts the configuration from json to protobuf +func (ipt *Iptables) Serialize() (*protocol.SysFirewall, error) { + sysfw := &protocol.SysFirewall{} + jun := jsonpb.Unmarshaler{ + AllowUnknownFields: true, + } + rawConfig, err := json.Marshal(&ipt.SysConfig) + if err != nil { + log.Error("nfables.Serialize() struct to string error: %s", err) + return nil, err + } + // string to proto + if err := jun.Unmarshal(strings.NewReader(string(rawConfig)), sysfw); err != nil { + log.Error("nfables.Serialize() string to protobuf error: %s", err) + return nil, err + } + + return sysfw, nil +} + +// Deserialize converts a protocolbuffer structure to json. +func (ipt *Iptables) Deserialize(sysfw *protocol.SysFirewall) ([]byte, error) { + jun := jsonpb.Marshaler{ + OrigName: true, + EmitDefaults: false, + Indent: " ", + } + + var b bytes.Buffer + if err := jun.Marshal(&b, sysfw); err != nil { + log.Error("nfables.Deserialize() error 2: %s", err) + return nil, err + } + return b.Bytes(), nil + + //return nil, fmt.Errorf("iptables.Deserialize() not implemented") +} diff --git a/daemon/firewall/iptables/monitor.go b/daemon/firewall/iptables/monitor.go new file mode 100644 index 0000000..fbcec2f --- /dev/null +++ b/daemon/firewall/iptables/monitor.go @@ -0,0 +1,69 @@ +package iptables + +import ( + "github.com/evilsocket/opensnitch/daemon/core" + "github.com/evilsocket/opensnitch/daemon/firewall/common" + "github.com/evilsocket/opensnitch/daemon/log" +) + +// AreRulesLoaded checks if the firewall rules for intercept traffic are loaded. +func (ipt *Iptables) AreRulesLoaded() bool { + var outMangle6 string + + outMangle, err := core.Exec("iptables", []string{"-n", "-L", "OUTPUT", "-t", "mangle"}) + if err != nil { + return false + } + + if core.IPv6Enabled { + outMangle6, err = core.Exec("ip6tables", []string{"-n", "-L", "OUTPUT", "-t", "mangle"}) + if err != nil { + return false + } + } + + systemRulesLoaded := true + ipt.chains.RLock() + if len(ipt.chains.Rules) > 0 { + for _, rule := range ipt.chains.Rules { + if chainOut4, err4 := core.Exec("iptables", []string{"-n", "-L", rule.Chain, "-t", rule.Table}); err4 == nil { + if ipt.regexSystemRulesQuery.FindString(chainOut4) == "" { + systemRulesLoaded = false + break + } + } + if core.IPv6Enabled { + if chainOut6, err6 := core.Exec("ip6tables", []string{"-n", "-L", rule.Chain, "-t", rule.Table}); err6 == nil { + if ipt.regexSystemRulesQuery.FindString(chainOut6) == "" { + systemRulesLoaded = false + break + } + } + } + } + } + ipt.chains.RUnlock() + + result := ipt.regexRulesQuery.FindString(outMangle) != "" && + systemRulesLoaded + + if core.IPv6Enabled { + result = result && ipt.regexRulesQuery.FindString(outMangle6) != "" + } + + return result +} + +// reloadRulesCallback gets called when the interception rules are not present or after the configuration file changes. +func (ipt *Iptables) reloadRulesCallback() { + log.Important("firewall rules changed, reloading") + ipt.CleanRules(false) + ipt.AddSystemRules(common.ReloadRules, common.BackupChains) + ipt.EnableInterception() +} + +// preloadConfCallback gets called before the fw configuration is reloaded +func (ipt *Iptables) preloadConfCallback() { + log.Info("iptables config changed, reloading") + ipt.DeleteSystemRules(common.ForcedDelRules, common.BackupChains, log.GetLogLevel() == log.DEBUG) +} diff --git a/daemon/firewall/iptables/rules.go b/daemon/firewall/iptables/rules.go new file mode 100644 index 0000000..6eed842 --- /dev/null +++ b/daemon/firewall/iptables/rules.go @@ -0,0 +1,77 @@ +package iptables + +import ( + "fmt" + + "github.com/evilsocket/opensnitch/daemon/core" + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/vishvananda/netlink" +) + +// RunRule inserts or deletes a firewall rule. +func (ipt *Iptables) RunRule(action Action, enable bool, logError bool, rule []string) (err4, err6 error) { + if enable == false { + action = "-D" + } + + rule = append([]string{string(action)}, rule...) + + ipt.Lock() + defer ipt.Unlock() + + if _, err4 = core.Exec(ipt.bin, rule); err4 != nil { + if logError { + log.Error("Error while running firewall rule, ipv4 err: %s", err4) + log.Error("rule: %s", rule) + } + } + + // On some systems IPv6 is disabled + if core.IPv6Enabled { + if _, err6 = core.Exec(ipt.bin6, rule); err6 != nil { + if logError { + log.Error("Error while running firewall rule, ipv6 err: %s", err6) + log.Error("rule: %s", rule) + } + } + } + + return +} + +// QueueDNSResponses redirects DNS responses to us, in order to keep a cache +// of resolved domains. +// INPUT --protocol udp --sport 53 -j NFQUEUE --queue-num 0 --queue-bypass +func (ipt *Iptables) QueueDNSResponses(enable bool, logError bool) (err4, err6 error) { + return ipt.RunRule(INSERT, enable, logError, []string{ + "INPUT", + "--protocol", "udp", + "--sport", "53", + "-j", "NFQUEUE", + "--queue-num", fmt.Sprintf("%d", ipt.QueueNum), + "--queue-bypass", + }) +} + +// QueueConnections inserts the firewall rule which redirects connections to us. +// Connections are queued until the user denies/accept them, or reaches a timeout. +// OUTPUT -t mangle -m conntrack --ctstate NEW,RELATED -j NFQUEUE --queue-num 0 --queue-bypass +func (ipt *Iptables) QueueConnections(enable bool, logError bool) (error, error) { + err4, err6 := ipt.RunRule(ADD, enable, logError, []string{ + "OUTPUT", + "-t", "mangle", + "-m", "conntrack", + "--ctstate", "NEW,RELATED", + "-j", "NFQUEUE", + "--queue-num", fmt.Sprintf("%d", ipt.QueueNum), + "--queue-bypass", + }) + if enable { + // flush conntrack as soon as netfilter rule is set. This ensures that already-established + // connections will go to netfilter queue. + if err := netlink.ConntrackTableFlush(netlink.ConntrackTable); err != nil { + log.Error("error in ConntrackTableFlush %s", err) + } + } + return err4, err6 +} diff --git a/daemon/firewall/iptables/system.go b/daemon/firewall/iptables/system.go new file mode 100644 index 0000000..9626787 --- /dev/null +++ b/daemon/firewall/iptables/system.go @@ -0,0 +1,155 @@ +package iptables + +import ( + "strings" + + "github.com/evilsocket/opensnitch/daemon/firewall/common" + "github.com/evilsocket/opensnitch/daemon/firewall/config" +) + +// CreateSystemRule creates the custom firewall chains and adds them to the system. +func (ipt *Iptables) CreateSystemRule(rule *config.FwRule, table, chain, hook string, logErrors bool) bool { + ipt.chains.Lock() + defer ipt.chains.Unlock() + if rule == nil { + return false + } + if table == "" { + table = "filter" + } + if hook == "" { + hook = rule.Chain + } + + chainName := SystemRulePrefix + "-" + hook + if _, ok := ipt.chains.Rules[table+"-"+chainName]; ok { + return false + } + ipt.RunRule(NEWCHAIN, common.EnableRule, logErrors, []string{chainName, "-t", table}) + + // Insert the rule at the top of the chain + if err4, err6 := ipt.RunRule(INSERT, common.EnableRule, logErrors, []string{hook, "-t", table, "-j", chainName}); err4 == nil && err6 == nil { + ipt.chains.Rules[table+"-"+chainName] = &SystemRule{ + Table: table, + Chain: chain, + Rule: rule, + } + } + return true + +} + +// AddSystemRules creates the system firewall from configuration. +func (ipt *Iptables) AddSystemRules(reload, backupExistingChains bool) { + // Version 0 has no Enabled field, so it'd be always false + if ipt.SysConfig.Enabled == false && ipt.SysConfig.Version > 0 { + return + } + + for _, cfg := range ipt.SysConfig.SystemRules { + if cfg.Rule != nil { + ipt.CreateSystemRule(cfg.Rule, cfg.Rule.Table, cfg.Rule.Chain, cfg.Rule.Chain, common.EnableRule) + ipt.AddSystemRule(ADD, cfg.Rule, cfg.Rule.Table, cfg.Rule.Chain, common.EnableRule) + continue + } + + if cfg.Chains != nil { + for _, chn := range cfg.Chains { + if chn.Hook != "" && chn.Type != "" { + ipt.ConfigureChainPolicy(chn.Type, chn.Hook, chn.Policy, true) + } + } + } + } +} + +// DeleteSystemRules deletes the system rules. +// If force is false and the rule has not been previously added, +// it won't try to delete the rules. Otherwise it'll try to delete them. +func (ipt *Iptables) DeleteSystemRules(force, backupExistingChains, logErrors bool) { + ipt.chains.Lock() + defer ipt.chains.Unlock() + + for _, fwCfg := range ipt.SysConfig.SystemRules { + if fwCfg.Rule == nil { + continue + } + chain := SystemRulePrefix + "-" + fwCfg.Rule.Chain + if _, ok := ipt.chains.Rules[fwCfg.Rule.Table+"-"+chain]; !ok && !force { + continue + } + ipt.RunRule(FLUSH, common.EnableRule, false, []string{chain, "-t", fwCfg.Rule.Table}) + ipt.RunRule(DELETE, !common.EnableRule, logErrors, []string{fwCfg.Rule.Chain, "-t", fwCfg.Rule.Table, "-j", chain}) + ipt.RunRule(DELCHAIN, common.EnableRule, false, []string{chain, "-t", fwCfg.Rule.Table}) + delete(ipt.chains.Rules, fwCfg.Rule.Table+"-"+chain) + + for _, chn := range fwCfg.Chains { + if chn.Table == "" { + chn.Table = "filter" + } + chain := SystemRulePrefix + "-" + chn.Hook + if _, ok := ipt.chains.Rules[chn.Type+"-"+chain]; !ok && !force { + continue + } + + ipt.RunRule(FLUSH, common.EnableRule, logErrors, []string{chain, "-t", chn.Type}) + ipt.RunRule(DELETE, !common.EnableRule, logErrors, []string{chn.Hook, "-t", chn.Type, "-j", chain}) + ipt.RunRule(DELCHAIN, common.EnableRule, logErrors, []string{chain, "-t", chn.Type}) + delete(ipt.chains.Rules, chn.Type+"-"+chain) + + } + } +} + +// DeleteSystemRule deletes a new rule. +func (ipt *Iptables) DeleteSystemRule(action Action, rule *config.FwRule, table, chain string, enable bool) (err4, err6 error) { + chainName := SystemRulePrefix + "-" + chain + if table == "" { + table = "filter" + } + r := []string{chainName, "-t", table} + if rule.Parameters != "" { + r = append(r, strings.Split(rule.Parameters, " ")...) + } + r = append(r, []string{"-j", rule.Target}...) + if rule.TargetParameters != "" { + r = append(r, strings.Split(rule.TargetParameters, " ")...) + } + + return ipt.RunRule(action, enable, true, r) +} + +// AddSystemRule inserts a new rule. +func (ipt *Iptables) AddSystemRule(action Action, rule *config.FwRule, table, chain string, enable bool) (err4, err6 error) { + if rule == nil { + return nil, nil + } + ipt.RLock() + defer ipt.RUnlock() + + chainName := SystemRulePrefix + "-" + chain + if table == "" { + table = "filter" + } + r := []string{chainName, "-t", table} + if rule.Parameters != "" { + r = append(r, strings.Split(rule.Parameters, " ")...) + } + r = append(r, []string{"-j", rule.Target}...) + if rule.TargetParameters != "" { + r = append(r, strings.Split(rule.TargetParameters, " ")...) + } + + return ipt.RunRule(ADD, enable, true, r) +} + +// ConfigureChainPolicy configures chains policy. +func (ipt *Iptables) ConfigureChainPolicy(table, hook, policy string, logError bool) { + // TODO: list all policies before modify them, and restore the original state on exit. + // still, if we exit abruptly, we might left the system badly configured. + ipt.RunRule(POLICY, true, logError, []string{ + hook, + strings.ToUpper(policy), + "-t", table, + }) +} diff --git a/daemon/firewall/nftables/chains.go b/daemon/firewall/nftables/chains.go new file mode 100644 index 0000000..e4a7921 --- /dev/null +++ b/daemon/firewall/nftables/chains.go @@ -0,0 +1,188 @@ +package nftables + +import ( + "fmt" + "strings" + + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/google/nftables" +) + +// getChainKey returns the identifier that will be used to link chains and rules. +// When adding a new chain the key is stored, then later when adding a rule we get +// the chain that the rule belongs to by this key. +func getChainKey(name string, table *nftables.Table) string { + if table == nil { + return "" + } + return fmt.Sprintf("%s-%s-%d", name, table.Name, table.Family) +} + +// GetChain gets an existing chain +func GetChain(name string, table *nftables.Table) *nftables.Chain { + key := getChainKey(name, table) + if ch, ok := sysChains.Load(key); ok { + return ch.(*nftables.Chain) + } + return nil +} + +// AddChain adds a new chain to nftables. +// https://wiki.nftables.org/wiki-nftables/index.php/Netfilter_hooks#Priority_within_hook +func (n *Nft) AddChain(name, table, family string, priority *nftables.ChainPriority, ctype nftables.ChainType, hook *nftables.ChainHook, policy nftables.ChainPolicy) *nftables.Chain { + if family == "" { + family = exprs.NFT_FAMILY_INET + } + tbl := n.GetTable(table, family) + if tbl == nil { + log.Error("%s addChain, Error getting table: %s, %s", logTag, table, family) + return nil + } + + var chain *nftables.Chain + // Verify if the chain already exists, and reuse it if it does. + // In some systems it fails adding a chain when it already exists, whilst in others + // it doesn't. + key := getChainKey(name, tbl) + chain = n.GetChain(name, tbl, family) + if chain != nil { + if _, exists := sysChains.Load(key); exists { + sysChains.Delete(key) + } + chain.Policy = &policy + n.Conn.AddChain(chain) + } else { + // nft list chains + chain = n.Conn.AddChain(&nftables.Chain{ + Name: strings.ToLower(name), + Table: tbl, + Type: ctype, + Hooknum: hook, + Priority: priority, + Policy: &policy, + }) + if chain == nil { + log.Debug("%s AddChain() chain == nil", logTag) + return nil + } + } + + sysChains.Store(key, chain) + return chain +} + +// GetChain checks if a chain in the given table exists. +func (n *Nft) GetChain(name string, table *nftables.Table, family string) *nftables.Chain { + if chains, err := n.Conn.ListChains(); err == nil { + for _, c := range chains { + if name == c.Name && table.Name == c.Table.Name && GetFamilyCode(family) == c.Table.Family { + return c + } + } + } + return nil +} + +// regular chains are user-defined chains, to better organize fw rules. +// https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains#Adding_regular_chains +func (n *Nft) addRegularChain(name, table, family string) error { + tbl := n.GetTable(table, family) + if tbl == nil { + return fmt.Errorf("%s addRegularChain, Error getting table: %s, %s", logTag, table, family) + } + + chain := n.Conn.AddChain(&nftables.Chain{ + Name: name, + Table: tbl, + }) + if chain == nil { + return fmt.Errorf("%s error adding regular chain: %s", logTag, name) + } + key := getChainKey(name, tbl) + sysChains.Store(key, chain) + + return nil +} + +// AddInterceptionChains adds the needed chains to intercept traffic. +func (n *Nft) AddInterceptionChains() error { + var filterPolicy nftables.ChainPolicy + var manglePolicy nftables.ChainPolicy + filterPolicy = nftables.ChainPolicyAccept + manglePolicy = nftables.ChainPolicyAccept + + tbl := n.GetTable(exprs.NFT_CHAIN_FILTER, exprs.NFT_FAMILY_INET) + if tbl != nil { + key := getChainKey(exprs.NFT_HOOK_INPUT, tbl) + ch, found := sysChains.Load(key) + if key != "" && found { + filterPolicy = *ch.(*nftables.Chain).Policy + } + } + tbl = n.GetTable(exprs.NFT_CHAIN_MANGLE, exprs.NFT_FAMILY_INET) + if tbl != nil { + key := getChainKey(exprs.NFT_HOOK_OUTPUT, tbl) + ch, found := sysChains.Load(key) + if key != "" && found { + manglePolicy = *ch.(*nftables.Chain).Policy + } + } + + // nft list tables + n.AddChain(exprs.NFT_HOOK_INPUT, exprs.NFT_CHAIN_FILTER, exprs.NFT_FAMILY_INET, + nftables.ChainPriorityFilter, nftables.ChainTypeFilter, nftables.ChainHookInput, filterPolicy) + if !n.Commit() { + return fmt.Errorf("Error adding DNS interception chain input-filter-inet") + } + n.AddChain(exprs.NFT_HOOK_OUTPUT, exprs.NFT_CHAIN_MANGLE, exprs.NFT_FAMILY_INET, + nftables.ChainPriorityMangle, nftables.ChainTypeRoute, nftables.ChainHookOutput, manglePolicy) + if !n.Commit() { + log.Error("(1) Error adding interception chain mangle-output-inet, trying with type Filter instead of Route") + + // Workaround for kernels 4.x and maybe others. + // @see firewall/nftables/utils.go:GetChainPriority() + chainPrio, chainType := GetChainPriority(exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_MANGLE, exprs.NFT_HOOK_OUTPUT) + n.AddChain(exprs.NFT_HOOK_OUTPUT, exprs.NFT_CHAIN_MANGLE, exprs.NFT_FAMILY_INET, + chainPrio, chainType, nftables.ChainHookOutput, manglePolicy) + if !n.Commit() { + return fmt.Errorf("(2) Error adding interception chain mangle-output-inet with type Filter. Report it on github please, specifying the distro and the kernel") + } + } + + return nil +} + +// DelChain deletes a chain from the system. +func (n *Nft) DelChain(chain *nftables.Chain) error { + n.Conn.DelChain(chain) + sysChains.Delete(getChainKey(chain.Name, chain.Table)) + if !n.Commit() { + return fmt.Errorf("[nftables] error deleting chain %s, %s", chain.Name, chain.Table.Name) + } + + return nil +} + +// backupExistingChains saves chains with Accept policy. +// If the user configures the chain policy to Drop, we need to set it back to Accept, +// in order not to block incoming connections. +func (n *Nft) backupExistingChains() { + if chains, err := n.Conn.ListChains(); err == nil { + for _, c := range chains { + if c.Policy != nil && *c.Policy == nftables.ChainPolicyAccept { + log.Debug("%s backing up existing chain with policy ACCEPT: %s, %s", logTag, c.Name, c.Table.Name) + origSysChains[getChainKey(c.Name, c.Table)] = c + } + } + } +} + +func (n *Nft) restoreBackupChains() { + for _, c := range origSysChains { + log.Debug("%s Restoring chain policy to accept: %s, %s", logTag, c.Name, c.Table.Name) + *c.Policy = nftables.ChainPolicyAccept + n.Conn.AddChain(c) + } + n.Commit() +} diff --git a/daemon/firewall/nftables/chains_test.go b/daemon/firewall/nftables/chains_test.go new file mode 100644 index 0000000..31e25b5 --- /dev/null +++ b/daemon/firewall/nftables/chains_test.go @@ -0,0 +1,85 @@ +package nftables_test + +import ( + "testing" + + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/nftest" + "github.com/google/nftables" +) + +func TestChains(t *testing.T) { + nftest.SkipIfNotPrivileged(t) + + conn, newNS := nftest.OpenSystemConn(t) + defer nftest.CleanupSystemConn(t, newNS) + nftest.Fw.Conn = conn + + if nftest.Fw.AddInterceptionTables() != nil { + t.Error("Error adding interception tables") + } + + t.Run("AddChain", func(t *testing.T) { + filterPolicy := nftables.ChainPolicyAccept + chn := nftest.Fw.AddChain( + exprs.NFT_HOOK_INPUT, + exprs.NFT_CHAIN_FILTER, + exprs.NFT_FAMILY_INET, + nftables.ChainPriorityFilter, + nftables.ChainTypeFilter, + nftables.ChainHookInput, + filterPolicy) + if chn == nil { + t.Error("chain input-filter-inet not created") + } + if !nftest.Fw.Commit() { + t.Error("error adding input-filter-inet chain") + } + }) + + t.Run("getChain", func(t *testing.T) { + tblfilter := nftest.Fw.GetTable(exprs.NFT_CHAIN_FILTER, exprs.NFT_FAMILY_INET) + if tblfilter == nil { + t.Error("table filter-inet not created") + } + + chn := nftest.Fw.GetChain(exprs.NFT_HOOK_INPUT, tblfilter, exprs.NFT_FAMILY_INET) + if chn == nil { + t.Error("chain input-filter-inet not added") + } + }) + + t.Run("delChain", func(t *testing.T) { + tblfilter := nftest.Fw.GetTable(exprs.NFT_CHAIN_FILTER, exprs.NFT_FAMILY_INET) + if tblfilter == nil { + t.Error("table filter-inet not created") + } + + chn := nftest.Fw.GetChain(exprs.NFT_HOOK_INPUT, tblfilter, exprs.NFT_FAMILY_INET) + if chn == nil { + t.Error("chain input-filter-inet not added") + } + + if err := nftest.Fw.DelChain(chn); err != nil { + t.Error("error deleting chain input-filter-inet") + } + }) + + nftest.Fw.DelSystemTables() +} + +// TestAddInterceptionChains checks if the needed tables and chains have been created. +// We use 2: output-mangle-inet for intercepting outbound connections, and input-filter-inet for DNS responses interception +func TestAddInterceptionChains(t *testing.T) { + nftest.SkipIfNotPrivileged(t) + + if err := nftest.Fw.AddInterceptionTables(); err != nil { + t.Errorf("Error adding interception tables: %s", err) + } + + if err := nftest.Fw.AddInterceptionChains(); err != nil { + t.Errorf("Error adding interception chains: %s", err) + } + + nftest.Fw.DelSystemTables() +} diff --git a/daemon/firewall/nftables/exprs/counter.go b/daemon/firewall/nftables/exprs/counter.go new file mode 100644 index 0000000..1bc0a52 --- /dev/null +++ b/daemon/firewall/nftables/exprs/counter.go @@ -0,0 +1,15 @@ +package exprs + +import ( + "github.com/google/nftables/expr" +) + +// NewExprCounter returns a counter for packets or bytes. +func NewExprCounter(counterName string) *[]expr.Any { + return &[]expr.Any{ + &expr.Objref{ + Type: 1, + Name: counterName, + }, + } +} diff --git a/daemon/firewall/nftables/exprs/counter_test.go b/daemon/firewall/nftables/exprs/counter_test.go new file mode 100644 index 0000000..4fcf137 --- /dev/null +++ b/daemon/firewall/nftables/exprs/counter_test.go @@ -0,0 +1,53 @@ +package exprs_test + +import ( + "testing" + + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/nftest" + "github.com/google/nftables" +) + +func TestExprNamedCounter(t *testing.T) { + nftest.SkipIfNotPrivileged(t) + + conn, newNS := nftest.OpenSystemConn(t) + defer nftest.CleanupSystemConn(t, newNS) + nftest.Fw.Conn = conn + + // we must create the table before the counter object. + tbl, _ := nftest.Fw.AddTable("yyy", exprs.NFT_FAMILY_INET) + + nftest.Fw.Conn.AddObj( + &nftables.CounterObj{ + Table: &nftables.Table{ + Name: "yyy", + Family: nftables.TableFamilyINet, + }, + Name: "xxx-counter", + Bytes: 0, + Packets: 0, + }, + ) + + r, _ := nftest.AddTestRule(t, conn, exprs.NewExprCounter("xxx-counter")) + if r == nil { + t.Error("Error adding counter rule") + return + } + + objs, err := nftest.Fw.Conn.GetObjects(tbl) + if err != nil { + t.Errorf("Error retrieving objects from table %s: %s", tbl.Name, err) + } + if len(objs) != 1 { + t.Errorf("%d objects found, expected 1", len(objs)) + } + counter, ok := objs[0].(*nftables.CounterObj) + if !ok { + t.Errorf("returned Obj is not CounterObj: %+v", objs[0]) + } + if counter.Name != "xxx-counter" { + t.Errorf("CounterObj name differs: %s, expected 'xxx-counter'", counter.Name) + } +} diff --git a/daemon/firewall/nftables/exprs/ct.go b/daemon/firewall/nftables/exprs/ct.go new file mode 100644 index 0000000..4254429 --- /dev/null +++ b/daemon/firewall/nftables/exprs/ct.go @@ -0,0 +1,121 @@ +package exprs + +import ( + "fmt" + "strconv" + "strings" + + "github.com/evilsocket/opensnitch/daemon/firewall/config" + "github.com/google/nftables/binaryutil" + "github.com/google/nftables/expr" +) + +// Example https://github.com/google/nftables/blob/master/nftables_test.go#L1234 +// https://wiki.nftables.org/wiki-nftables/index.php/Setting_packet_metainformation + +// NewExprCtMark returns a new ct expression. +// # set +// # nft --debug netlink add rule filter output mark set 1 +// ip filter output +// [ immediate reg 1 0x00000001 ] +// [ meta set mark with reg 1 ] +// +// match mark: +// nft --debug netlink add rule mangle prerouting ct mark 123 +// [ ct load mark => reg 1 ] +// [ cmp eq reg 1 0x0000007b ] +func NewExprCtMark(setMark bool, value string, cmpOp *expr.CmpOp) (*[]expr.Any, error) { + mark, err := strconv.Atoi(value) + if err != nil { + return nil, fmt.Errorf("Invalid conntrack mark: %s (%s)", err, value) + } + + exprCtMark := []expr.Any{} + exprCtMark = append(exprCtMark, []expr.Any{ + &expr.Immediate{ + Register: 1, + Data: binaryutil.NativeEndian.PutUint32(uint32(mark)), + }, + &expr.Ct{ + Key: expr.CtKeyMARK, + Register: 1, + SourceRegister: setMark, + }, + }...) + if setMark == false { + exprCtMark = append(exprCtMark, []expr.Any{ + &expr.Cmp{Op: *cmpOp, Register: 1, Data: binaryutil.NativeEndian.PutUint32(uint32(mark))}, + }...) + } + + return &exprCtMark, nil +} + +// NewExprCtState returns a new ct expression. +func NewExprCtState(ctFlags []*config.ExprValues) (*[]expr.Any, error) { + mask := uint32(0) + + for _, flag := range ctFlags { + found, msk, err := parseInlineCtStates(flag.Value) + if err != nil { + return nil, err + } + if found { + mask |= msk + continue + } + + msk, err = getCtState(flag.Value) + if err != nil { + return nil, err + } + mask |= msk + } + + return &[]expr.Any{ + &expr.Ct{ + Register: 1, SourceRegister: false, Key: expr.CtKeySTATE, + }, + &expr.Bitwise{ + SourceRegister: 1, + DestRegister: 1, + Len: 4, + Mask: binaryutil.NativeEndian.PutUint32(mask), + Xor: binaryutil.NativeEndian.PutUint32(0), + }, + }, nil +} + +func parseInlineCtStates(flags string) (found bool, mask uint32, err error) { + // a "state" flag may be compounded of multiple values, separated by commas: + // related,established + fgs := strings.Split(flags, ",") + if len(fgs) > 0 { + for _, fg := range fgs { + msk, err := getCtState(fg) + if err != nil { + return false, 0, err + } + mask |= msk + found = true + } + } + return +} + +func getCtState(flag string) (mask uint32, err error) { + switch strings.ToLower(flag) { + case CT_STATE_NEW: + mask |= expr.CtStateBitNEW + case CT_STATE_ESTABLISHED: + mask |= expr.CtStateBitESTABLISHED + case CT_STATE_RELATED: + mask |= expr.CtStateBitRELATED + case CT_STATE_INVALID: + mask |= expr.CtStateBitINVALID + default: + return 0, fmt.Errorf("Invalid conntrack flag: %s", flag) + } + + return +} diff --git a/daemon/firewall/nftables/exprs/ct_test.go b/daemon/firewall/nftables/exprs/ct_test.go new file mode 100644 index 0000000..08a52c4 --- /dev/null +++ b/daemon/firewall/nftables/exprs/ct_test.go @@ -0,0 +1,224 @@ +package exprs_test + +import ( + "fmt" + "testing" + + "github.com/evilsocket/opensnitch/daemon/firewall/config" + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/nftest" + "github.com/google/nftables/binaryutil" + "github.com/google/nftables/expr" +) + +func TestExprCtMark(t *testing.T) { + nftest.SkipIfNotPrivileged(t) + + conn, newNS := nftest.OpenSystemConn(t) + defer nftest.CleanupSystemConn(t, newNS) + nftest.Fw.Conn = conn + + type ctTestsT struct { + nftest.TestsT + setMark bool + } + + cmp := expr.CmpOpEq + tests := []ctTestsT{ + { + TestsT: nftest.TestsT{ + Name: "test-ct-set-mark-666", + Parms: "666", + ExpectedExprsNum: 2, + ExpectedExprs: []interface{}{ + &expr.Immediate{ + Register: 1, + Data: binaryutil.NativeEndian.PutUint32(uint32(666)), + }, + &expr.Ct{ + Key: expr.CtKeyMARK, + Register: 1, + SourceRegister: true, + }, + }, + }, + setMark: true, + }, + { + TestsT: nftest.TestsT{ + Name: "test-ct-check-mark-666", + Parms: "666", + ExpectedExprsNum: 3, + ExpectedExprs: []interface{}{ + &expr.Immediate{ + Register: 1, + Data: binaryutil.NativeEndian.PutUint32(uint32(666)), + }, + &expr.Ct{ + Key: expr.CtKeyMARK, + Register: 1, + SourceRegister: false, + }, + &expr.Cmp{ + Op: cmp, + Register: 1, + Data: binaryutil.NativeEndian.PutUint32(uint32(666)), + }, + }, + }, + setMark: false, + }, + { + TestsT: nftest.TestsT{ + Name: "test-invalid-ct-check-mark", + Parms: "0x29a", + ExpectedExprsNum: 3, + ExpectedExprs: []interface{}{}, + ExpectedFail: true, + }, + setMark: false, + }, + } + + for _, test := range tests { + t.Run(test.Name, func(t *testing.T) { + quotaExpr, err := exprs.NewExprCtMark(test.setMark, test.TestsT.Parms, &cmp) + if err != nil && !test.ExpectedFail { + t.Errorf("Error creating expr Ct: %s", quotaExpr) + return + } else if err != nil && test.ExpectedFail { + return + } + + r, _ := nftest.AddTestRule(t, conn, quotaExpr) + if r == nil && !test.ExpectedFail { + t.Error("Error adding rule with Ct expression") + } + + if !nftest.AreExprsValid(t, &test.TestsT, r) { + return + } + + if test.ExpectedFail { + t.Errorf("test should have failed") + } + }) + } + +} + +func TestExprCtState(t *testing.T) { + nftest.SkipIfNotPrivileged(t) + + conn, newNS := nftest.OpenSystemConn(t) + defer nftest.CleanupSystemConn(t, newNS) + nftest.Fw.Conn = conn + + type ctTestsT struct { + nftest.TestsT + setMark bool + } + + tests := []nftest.TestsT{ + { + Name: "test-ct-single-state", + Parms: "", + Values: []*config.ExprValues{ + &config.ExprValues{ + Key: exprs.NFT_CT_STATE, + Value: exprs.CT_STATE_NEW, + }, + }, + ExpectedExprsNum: 2, + ExpectedExprs: []interface{}{ + &expr.Ct{ + Register: 1, SourceRegister: false, Key: expr.CtKeySTATE, + }, + &expr.Bitwise{ + SourceRegister: 1, + DestRegister: 1, + Len: 4, + Mask: binaryutil.NativeEndian.PutUint32(expr.CtStateBitNEW), + Xor: binaryutil.NativeEndian.PutUint32(0), + }, + }, + ExpectedFail: false, + }, + { + Name: "test-ct-multiple-states", + Parms: "", + Values: []*config.ExprValues{ + &config.ExprValues{ + Key: exprs.NFT_CT_STATE, + Value: fmt.Sprint(exprs.CT_STATE_NEW, ",", exprs.CT_STATE_ESTABLISHED), + }, + }, + ExpectedExprsNum: 2, + ExpectedExprs: []interface{}{ + &expr.Ct{ + Register: 1, SourceRegister: false, Key: expr.CtKeySTATE, + }, + &expr.Bitwise{ + SourceRegister: 1, + DestRegister: 1, + Len: 4, + Mask: binaryutil.NativeEndian.PutUint32(expr.CtStateBitNEW | expr.CtStateBitESTABLISHED), + Xor: binaryutil.NativeEndian.PutUint32(0), + }, + }, + ExpectedFail: false, + }, + { + Name: "test-invalid-ct-state", + Parms: "", + Values: []*config.ExprValues{ + &config.ExprValues{ + Key: exprs.NFT_CT_STATE, + Value: "xxx", + }, + }, + ExpectedExprsNum: 2, + ExpectedExprs: []interface{}{}, + ExpectedFail: true, + }, + { + Name: "test-invalid-ct-states", + Parms: "", + Values: []*config.ExprValues{ + &config.ExprValues{ + Key: exprs.NFT_CT_STATE, + Value: "new,xxx", + }, + }, + ExpectedExprsNum: 2, + ExpectedExprs: []interface{}{}, + ExpectedFail: true, + }, + } + + for _, test := range tests { + t.Run(test.Name, func(t *testing.T) { + quotaExpr, err := exprs.NewExprCtState(test.Values) + if err != nil && !test.ExpectedFail { + t.Errorf("Error creating expr Ct: %s", quotaExpr) + return + } else if err != nil && test.ExpectedFail { + return + } + + r, _ := nftest.AddTestRule(t, conn, quotaExpr) + if r == nil && !test.ExpectedFail { + t.Error("Error adding rule with Quota expression") + } + + if !nftest.AreExprsValid(t, &test, r) { + return + } + + if test.ExpectedFail { + t.Errorf("test should have failed") + } + }) + } + +} diff --git a/daemon/firewall/nftables/exprs/enums.go b/daemon/firewall/nftables/exprs/enums.go new file mode 100644 index 0000000..fbfce09 --- /dev/null +++ b/daemon/firewall/nftables/exprs/enums.go @@ -0,0 +1,192 @@ +package exprs + +// keywords used in the configuration to define rules. +const ( + // https://wiki.nftables.org/wiki-nftables/index.php/Netfilter_hooks#Priority_within_hook + NFT_CHAIN_MANGLE = "mangle" + NFT_CHAIN_FILTER = "filter" + NFT_CHAIN_RAW = "raw" + NFT_CHAIN_SECURITY = "security" + NFT_CHAIN_NATDEST = "natdest" + NFT_CHAIN_NATSOURCE = "natsource" + NFT_CHAIN_CONNTRACK = "conntrack" + NFT_CHAIN_SELINUX = "selinux" + + NFT_HOOK_INPUT = "input" + NFT_HOOK_OUTPUT = "output" + NFT_HOOK_PREROUTING = "prerouting" + NFT_HOOK_POSTROUTING = "postrouting" + NFT_HOOK_INGRESS = "ingress" + NFT_HOOK_EGRESS = "egress" + NFT_HOOK_FORWARD = "forward" + + NFT_TABLE_INET = "inet" + NFT_TABLE_NAT = "nat" + // TODO + NFT_TABLE_ARP = "arp" + NFT_TABLE_BRIDGE = "bridge" + NFT_TABLE_NETDEV = "netdev" + + NFT_FAMILY_IP = "ip" + NFT_FAMILY_IP6 = "ip6" + NFT_FAMILY_INET = "inet" + NFT_FAMILY_BRIDGE = "bridge" + NFT_FAMILY_ARP = "arp" + NFT_FAMILY_NETDEV = "netdev" + + VERDICT_ACCEPT = "accept" + VERDICT_DROP = "drop" + VERDICT_REJECT = "reject" + VERDICT_RETURN = "return" + VERDICT_QUEUE = "queue" + + VERDICT_JUMP = "jump" + // TODO + VERDICT_GOTO = "goto" + VERDICT_STOP = "stop" + VERDICT_STOLEN = "stolen" + VERDICT_CONTINUE = "continue" + VERDICT_MASQUERADE = "masquerade" + VERDICT_DNAT = "dnat" + VERDICT_SNAT = "snat" + VERDICT_REDIRECT = "redirect" + VERDICT_TPROXY = "tproxy" + + NFT_PARM_TO = "to" + + NFT_QUEUE_NUM = "num" + NFT_QUEUE_BY_PASS = "queue-bypass" + + NFT_MASQ_RANDOM = "random" + NFT_MASQ_FULLY_RANDOM = "fully-random" + NFT_MASQ_PERSISTENT = "persistent" + + NFT_PROTOCOL = "protocol" + NFT_SPORT = "sport" + NFT_DPORT = "dport" + NFT_SADDR = "saddr" + NFT_DADDR = "daddr" + NFT_ICMP_CODE = "code" + NFT_ICMP_TYPE = "type" + + NFT_ETHER = "ether" + + NFT_IIFNAME = "iifname" + NFT_OIFNAME = "oifname" + + NFT_LOG = "log" + NFT_LOG_PREFIX = "prefix" + // TODO + NFT_LOG_LEVEL = "level" + NFT_LOG_LEVEL_EMERG = "emerg" + NFT_LOG_LEVEL_ALERT = "alert" + NFT_LOG_LEVEL_CRIT = "crit" + NFT_LOG_LEVEL_ERR = "err" + NFT_LOG_LEVEL_WARN = "warn" + NFT_LOG_LEVEL_NOTICE = "notice" + NFT_LOG_LEVEL_INFO = "info" + NFT_LOG_LEVEL_DEBUG = "debug" + NFT_LOG_LEVEL_AUDIT = "audit" + NFT_LOG_FLAGS = "flags" + + NFT_CT = "ct" + NFT_CT_STATE = "state" + NFT_CT_SET_MARK = "set" + NFT_CT_MARK = "mark" + CT_STATE_NEW = "new" + CT_STATE_ESTABLISHED = "established" + CT_STATE_RELATED = "related" + CT_STATE_INVALID = "invalid" + + NFT_NOTRACK = "notrack" + + NFT_QUOTA = "quota" + NFT_QUOTA_UNTIL = "until" + NFT_QUOTA_OVER = "over" + NFT_QUOTA_USED = "used" + NFT_QUOTA_UNIT_BYTES = "bytes" + NFT_QUOTA_UNIT_KB = "kbytes" + NFT_QUOTA_UNIT_MB = "mbytes" + NFT_QUOTA_UNIT_GB = "gbytes" + + NFT_COUNTER = "counter" + NFT_COUNTER_NAME = "name" + NFT_COUNTER_PACKETS = "packets" + NFT_COUNTER_BYTES = "bytes" + + NFT_LIMIT = "limit" + NFT_LIMIT_OVER = "over" + NFT_LIMIT_BURST = "burst" + NFT_LIMIT_UNITS_RATE = "rate-units" + NFT_LIMIT_UNITS_TIME = "time-units" + NFT_LIMIT_UNITS = "units" + NFT_LIMIT_UNIT_SECOND = "second" + NFT_LIMIT_UNIT_MINUTE = "minute" + NFT_LIMIT_UNIT_HOUR = "hour" + NFT_LIMIT_UNIT_DAY = "day" + NFT_LIMIT_UNIT_KBYTES = "kbytes" + NFT_LIMIT_UNIT_MBYTES = "mbytes" + + NFT_META = "meta" + NFT_META_MARK = "mark" + NFT_META_SET_MARK = "set" + NFT_META_PRIORITY = "priority" + NFT_META_NFTRACE = "nftrace" + NFT_META_SET = "set" + NFT_META_SKUID = "skuid" + NFT_META_SKGID = "skgid" + NFT_META_L4PROTO = "l4proto" + NFT_META_PROTOCOL = "protocol" + + NFT_PROTO_UDP = "udp" + NFT_PROTO_UDPLITE = "udplite" + NFT_PROTO_TCP = "tcp" + NFT_PROTO_SCTP = "sctp" + NFT_PROTO_DCCP = "dccp" + NFT_PROTO_ICMP = "icmp" + NFT_PROTO_ICMPX = "icmpx" + NFT_PROTO_ICMPv6 = "icmpv6" + NFT_PROTO_AH = "ah" + NFT_PROTO_ETHERNET = "ethernet" + NFT_PROTO_GRE = "gre" + NFT_PROTO_IP = "ip" + NFT_PROTO_IPIP = "ipip" + NFT_PROTO_L2TP = "l2tp" + NFT_PROTO_COMP = "comp" + NFT_PROTO_IGMP = "igmp" + NFT_PROTO_ESP = "esp" + NFT_PROTO_RAW = "raw" + NFT_PROTO_ENCAP = "encap" + + ICMP_NO_ROUTE = "no-route" + ICMP_PROT_UNREACHABLE = "prot-unreachable" + ICMP_PORT_UNREACHABLE = "port-unreachable" + ICMP_NET_UNREACHABLE = "net-unreachable" + ICMP_ADDR_UNREACHABLE = "addr-unreachable" + ICMP_HOST_UNREACHABLE = "host-unreachable" + ICMP_NET_PROHIBITED = "net-prohibited" + ICMP_HOST_PROHIBITED = "host-prohibited" + ICMP_ADMIN_PROHIBITED = "admin-prohibited" + ICMP_REJECT_ROUTE = "reject-route" + ICMP_REJECT_POLICY_FAIL = "policy-fail" + + ICMP_ECHO_REPLY = "echo-reply" + ICMP_ECHO_REQUEST = "echo-request" + ICMP_SOURCE_QUENCH = "source-quench" + ICMP_DEST_UNREACHABLE = "destination-unreachable" + ICMP_REDIRECT = "redirect" + ICMP_TIME_EXCEEDED = "time-exceeded" + ICMP_INFO_REQUEST = "info-request" + ICMP_INFO_REPLY = "info-reply" + ICMP_PARAMETER_PROBLEM = "parameter-problem" + ICMP_TIMESTAMP_REQUEST = "timestamp-request" + ICMP_TIMESTAMP_REPLY = "timestamp-reply" + ICMP_ROUTER_ADVERTISEMENT = "router-advertisement" + ICMP_ROUTER_SOLICITATION = "router-solicitation" + ICMP_ADDRESS_MASK_REQUEST = "address-mask-request" + ICMP_ADDRESS_MASK_REPLY = "address-mask-reply" + + ICMP_PACKET_TOO_BIG = "packet-too-big" + ICMP_NEIGHBOUR_SOLICITATION = "neighbour-solicitation" + ICMP_NEIGHBOUR_ADVERTISEMENT = "neighbour-advertisement" +) diff --git a/daemon/firewall/nftables/exprs/ether.go b/daemon/firewall/nftables/exprs/ether.go new file mode 100644 index 0000000..dfad247 --- /dev/null +++ b/daemon/firewall/nftables/exprs/ether.go @@ -0,0 +1,64 @@ +package exprs + +import ( + "encoding/hex" + "fmt" + "strings" + + "github.com/evilsocket/opensnitch/daemon/firewall/config" + "github.com/google/nftables/expr" +) + +// NewExprEther creates a new expression to match ethernet MAC addresses +func NewExprEther(values []*config.ExprValues) (*[]expr.Any, error) { + etherExpr := []expr.Any{} + macDir := uint32(6) + + for _, eth := range values { + if eth.Key == NFT_DADDR { + macDir = uint32(0) + } else { + macDir = uint32(6) + } + macaddr, err := parseMACAddr(eth.Value) + if err != nil { + return nil, err + } + etherExpr = append(etherExpr, []expr.Any{ + &expr.Meta{Key: expr.MetaKeyIIFTYPE, Register: 1}, + &expr.Cmp{ + Op: expr.CmpOpEq, + Register: 1, + Data: []byte{0x01, 0x00}, + }, + &expr.Payload{ + DestRegister: 1, + Base: expr.PayloadBaseLLHeader, + Offset: macDir, + Len: 6, + }, + &expr.Cmp{ + Op: expr.CmpOpEq, + Register: 1, + Data: macaddr, + }, + }...) + } + return ðerExpr, nil +} + +func parseMACAddr(macValue string) ([]byte, error) { + mac := strings.Split(macValue, ":") + macaddr := make([]byte, 0) + if len(mac) != 6 { + return nil, fmt.Errorf("Invalid MAC address: %s", macValue) + } + for i, m := range mac { + mm, err := hex.DecodeString(m) + if err != nil { + return nil, fmt.Errorf("Invalid MAC byte: %c (%s)", mm[i], macValue) + } + macaddr = append(macaddr, mm[0]) + } + return macaddr, nil +} diff --git a/daemon/firewall/nftables/exprs/ether_test.go b/daemon/firewall/nftables/exprs/ether_test.go new file mode 100644 index 0000000..998e1a9 --- /dev/null +++ b/daemon/firewall/nftables/exprs/ether_test.go @@ -0,0 +1,105 @@ +package exprs_test + +import ( + "bytes" + "reflect" + "testing" + + "github.com/evilsocket/opensnitch/daemon/firewall/config" + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/nftest" + "github.com/google/nftables/expr" +) + +func TestExprEther(t *testing.T) { + nftest.SkipIfNotPrivileged(t) + + conn, newNS := nftest.OpenSystemConn(t) + defer nftest.CleanupSystemConn(t, newNS) + nftest.Fw.Conn = conn + + values := []*config.ExprValues{ + &config.ExprValues{ + Key: "ether", + Value: "de:ad:be:af:ca:fe", + }, + } + + etherExpr, err := exprs.NewExprEther(values) + if err != nil { + t.Errorf("Error creating Ether expression: %s, %+v", err, values) + } + + r, _ := nftest.AddTestRule(t, conn, etherExpr) + if r == nil { + t.Error("Error adding Ether rule") + return + } + if len(r.Exprs) != 4 { + t.Errorf("invalid rule created, we expected 4 expressions, got: %d", len(r.Exprs)) + } + + /* + expr Meta + expr Cmp + expr Payload + expr Cmp + */ + + t.Run("test-ether-expr meta", func(t *testing.T) { + e := r.Exprs[0] // meta + if reflect.TypeOf(e).String() != "*expr.Meta" { + t.Errorf("first expression should be *expr.Meta, instead of: %s", reflect.TypeOf(e)) + } + lMeta, ok := e.(*expr.Meta) + if !ok { + t.Errorf("invalid meta expr: %T", e) + } + if lMeta.Key != expr.MetaKeyIIFTYPE { + t.Errorf("invalid meta Key: %d, instead of %d", lMeta.Key, expr.MetaKeyIIFTYPE) + } + }) + + t.Run("test-ether-expr cmp", func(t *testing.T) { + e := r.Exprs[1] // cmp + if reflect.TypeOf(e).String() != "*expr.Cmp" { + t.Errorf("second expression should be *expr.Cmp, instead of: %s", reflect.TypeOf(e)) + } + lCmp, ok := e.(*expr.Cmp) + if !ok { + t.Errorf("invalid cmp expr: %T", e) + } + if !bytes.Equal(lCmp.Data, []byte{0x01, 0x00}) { + t.Errorf("invalid cmp data: %v", lCmp.Data) + } + }) + + t.Run("test-ether-expr payload", func(t *testing.T) { + e := r.Exprs[2] // payload + if reflect.TypeOf(e).String() != "*expr.Payload" { + t.Errorf("third expression should be *expr.Payload, instead of: %s", reflect.TypeOf(e)) + } + lPayload, ok := e.(*expr.Payload) + if !ok { + t.Errorf("invalid payload expr: %T", e) + } + if lPayload.Base != expr.PayloadBaseLLHeader || lPayload.Offset != 6 || lPayload.Len != 6 { + t.Errorf("invalid payload data: %v", lPayload) + } + }) + + t.Run("test-ether-expr cmp", func(t *testing.T) { + e := r.Exprs[3] // cmp + if reflect.TypeOf(e).String() != "*expr.Cmp" { + t.Errorf("fourth expression should be *expr.Cmp, instead of: %s", reflect.TypeOf(e)) + } + lCmp, ok := e.(*expr.Cmp) + if !ok { + t.Errorf("invalid cmp expr: %T", e) + } + if !bytes.Equal(lCmp.Data, []byte{222, 173, 190, 175, 202, 254}) { + t.Errorf("invalid cmp data: %q", lCmp.Data) + } + }) + +} diff --git a/daemon/firewall/nftables/exprs/iface.go b/daemon/firewall/nftables/exprs/iface.go new file mode 100644 index 0000000..66d2637 --- /dev/null +++ b/daemon/firewall/nftables/exprs/iface.go @@ -0,0 +1,33 @@ +package exprs + +import ( + "github.com/google/nftables/expr" +) + +// NewExprIface returns a new network interface expression +func NewExprIface(iface string, isOut bool, cmpOp expr.CmpOp) *[]expr.Any { + keyDev := expr.MetaKeyIIFNAME + if isOut { + keyDev = expr.MetaKeyOIFNAME + } + return &[]expr.Any{ + &expr.Meta{Key: keyDev, Register: 1}, + &expr.Cmp{ + Op: cmpOp, + Register: 1, + Data: ifname(iface), + }, + } +} + +// https://github.com/google/nftables/blob/master/nftables_test.go#L81 +func ifname(n string) []byte { + buf := make([]byte, 16) + length := len(n) + // allow wildcards + if n[length-1:] == "*" { + return []byte(n[:length-1]) + } + copy(buf, []byte(n+"\x00")) + return buf +} diff --git a/daemon/firewall/nftables/exprs/iface_test.go b/daemon/firewall/nftables/exprs/iface_test.go new file mode 100644 index 0000000..17a47c4 --- /dev/null +++ b/daemon/firewall/nftables/exprs/iface_test.go @@ -0,0 +1,80 @@ +package exprs_test + +import ( + "bytes" + "reflect" + "testing" + + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/nftest" + "github.com/google/nftables/expr" +) + +// https://github.com/evilsocket/opensnitch/blob/master/daemon/firewall/nftables/exprs/iface.go#L22 +func ifname(n string) []byte { + buf := make([]byte, 16) + length := len(n) + // allow wildcards + if n[length-1:] == "*" { + return []byte(n[:length-1]) + } + copy(buf, []byte(n+"\x00")) + return buf +} + +func TestExprIface(t *testing.T) { + nftest.SkipIfNotPrivileged(t) + + conn, newNS := nftest.OpenSystemConn(t) + defer nftest.CleanupSystemConn(t, newNS) + nftest.Fw.Conn = conn + + type ifaceTestsT struct { + name string + iface string + out bool + } + tests := []ifaceTestsT{ + {"test-in-iface-xxx", "in-iface0", false}, + {"test-out-iface-xxx", "out-iface0", true}, + {"test-out-iface-xxx-wildcard", "out-iface*", true}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + ifaceExpr := exprs.NewExprIface(test.iface, test.out, expr.CmpOpEq) + r, _ := nftest.AddTestRule(t, conn, ifaceExpr) + if r == nil { + t.Error("Error adding rule with iface expression") + } + if total := len(r.Exprs); total != 2 { + t.Errorf("expected 2 expressions, got %d: %+v", total, r.Exprs) + } + e := r.Exprs[0] + if reflect.TypeOf(e).String() != "*expr.Meta" { + t.Errorf("first expression should be *expr.Meta, instead of: %s", reflect.TypeOf(e)) + } + lExpr, ok := e.(*expr.Meta) + if !ok { + t.Errorf("invalid iface meta expr: %T", e) + } + if test.out && lExpr.Key != expr.MetaKeyOIFNAME { + t.Errorf("iface Key should be MetaKeyOIFNAME instead of: %+v", lExpr) + } else if !test.out && lExpr.Key != expr.MetaKeyIIFNAME { + t.Errorf("iface Key should be MetaKeyIIFNAME instead of: %+v", lExpr) + } + + e = r.Exprs[1] + if reflect.TypeOf(e).String() != "*expr.Cmp" { + t.Errorf("second expression should be *expr.Cmp, instead of: %s", reflect.TypeOf(e)) + } + lCmp, ok := e.(*expr.Cmp) + if !ok { + t.Errorf("invalid iface cmp expr: %T", e) + } + if !bytes.Equal(lCmp.Data, ifname(test.iface)) { + t.Errorf("iface Cmp does not match: %v, expected: %v", lCmp.Data, ifname(test.iface)) + } + }) + } +} diff --git a/daemon/firewall/nftables/exprs/ip.go b/daemon/firewall/nftables/exprs/ip.go new file mode 100644 index 0000000..c11f5af --- /dev/null +++ b/daemon/firewall/nftables/exprs/ip.go @@ -0,0 +1,147 @@ +package exprs + +import ( + "fmt" + "net" + "strings" + + "github.com/evilsocket/opensnitch/daemon/firewall/config" + "github.com/google/nftables/expr" + "golang.org/x/sys/unix" +) + +// NewExprIP returns a new IP expression. +// You can use multiple statements to specify daddr + saddr, or combine them +// in a single statement expression: +// Example 1 (filtering by source and dest address): +// "Name": "ip", +// "Values": [ {"Key": "saddr": "Value": "1.2.3.4"},{"Key": "daddr": "Value": "1.2.3.5"} ] +// Example 2 (filtering by multiple dest addrs IPs): +// "Name": "ip", +// "Values": [ +// {"Key": "daddr": "Value": "1.2.3.4"}, +// {"Key": "daddr": "Value": "1.2.3.5"} +// ] +// Example 3 (filtering by network range): +// "Name": "ip", +// "Values": [ +// {"Key": "daddr": "Value": "1.2.3.4-1.2.9.254"} +// ] +// TODO (filter by multiple dest addrs separated by commas): +// "Values": [ +// {"Key": "daddr": "Value": "1.2.3.4,1.2.9.254"} +// ] +func NewExprIP(family string, ipOptions []*config.ExprValues, cmpOp expr.CmpOp) (*[]expr.Any, error) { + var exprIP []expr.Any + + // if the table family is inet, we need to specify the protocol of the IP being added. + if family == NFT_FAMILY_INET { + exprIP = append(exprIP, &expr.Meta{Key: expr.MetaKeyNFPROTO, Register: 1}) + exprIP = append(exprIP, &expr.Cmp{Op: expr.CmpOpEq, Register: 1, Data: []byte{unix.NFPROTO_IPV4}}) + } + for _, ipOpt := range ipOptions { + // TODO: ipv6 + switch ipOpt.Key { + case NFT_SADDR, NFT_DADDR: + payload := getExprIPPayload(ipOpt.Key) + exprIP = append(exprIP, payload) + if strings.Index(ipOpt.Value, "-") == -1 { + exprIPtemp, err := getExprIP(ipOpt.Value, cmpOp) + if err != nil { + return nil, err + } + exprIP = append(exprIP, *exprIPtemp...) + } else { + exprIPtemp, err := getExprRangeIP(ipOpt.Value, cmpOp) + if err != nil { + return nil, err + } + exprIP = append(exprIP, *exprIPtemp...) + } + + case NFT_PROTOCOL: + payload := getExprIPPayload(ipOpt.Key) + exprIP = append(exprIP, payload) + protoCode, err := getProtocolCode(ipOpt.Value) + if err != nil { + return nil, err + } + exprIP = append(exprIP, []expr.Any{ + &expr.Cmp{ + Op: cmpOp, + Register: 1, + Data: []byte{byte(protoCode)}, + }, + }...) + } + } + return &exprIP, nil +} + +func getExprIPPayload(what string) *expr.Payload { + + switch what { + case NFT_PROTOCOL: + return &expr.Payload{ + DestRegister: 1, + Offset: 9, // daddr + Base: expr.PayloadBaseNetworkHeader, + Len: 1, // 16 ipv6 + } + case NFT_DADDR: + // NOTE 1: if "what" is daddr and SourceRegister is part of the Payload{} expression, + // the rule is not added. + return &expr.Payload{ + DestRegister: 1, + Offset: 16, // daddr + Base: expr.PayloadBaseNetworkHeader, + Len: 4, // 16 ipv6 + } + + default: + return &expr.Payload{ + SourceRegister: 1, + DestRegister: 1, + Offset: 12, // saddr + Base: expr.PayloadBaseNetworkHeader, + Len: 4, // 16 ipv6 + } + } +} + +// Supported IP types: a.b.c.d, a.b.c.d-w.x.y.z +// TODO: support IPs separated by commas: a.b.c.d, e.f.g.h,... +func getExprIP(value string, cmpOp expr.CmpOp) (*[]expr.Any, error) { + ip := net.ParseIP(value) + if ip == nil { + return nil, fmt.Errorf("Invalid IP: %s", value) + } + + return &[]expr.Any{ + &expr.Cmp{ + Op: cmpOp, + Register: 1, + Data: ip.To4(), + }, + }, nil +} + +// Supported IP types: a.b.c.d, a.b.c.d-w.x.y.z +// TODO: support IPs separated by commas: a.b.c.d, e.f.g.h,... +func getExprRangeIP(value string, cmpOp expr.CmpOp) (*[]expr.Any, error) { + ips := strings.Split(value, "-") + ipSrc := net.ParseIP(ips[0]) + ipDst := net.ParseIP(ips[1]) + if ipSrc == nil || ipDst == nil { + return nil, fmt.Errorf("Invalid IPs range: %v", ips) + } + + return &[]expr.Any{ + &expr.Range{ + Op: cmpOp, + Register: 1, + FromData: ipSrc.To4(), + ToData: ipDst.To4(), + }, + }, nil +} diff --git a/daemon/firewall/nftables/exprs/ip_test.go b/daemon/firewall/nftables/exprs/ip_test.go new file mode 100644 index 0000000..e27b162 --- /dev/null +++ b/daemon/firewall/nftables/exprs/ip_test.go @@ -0,0 +1,354 @@ +package exprs_test + +import ( + "net" + "testing" + + "github.com/evilsocket/opensnitch/daemon/firewall/config" + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/nftest" + "github.com/google/nftables/expr" + "golang.org/x/sys/unix" +) + +func TestExprIP(t *testing.T) { + nftest.SkipIfNotPrivileged(t) + + conn, newNS := nftest.OpenSystemConn(t) + defer nftest.CleanupSystemConn(t, newNS) + nftest.Fw.Conn = conn + + tests := []nftest.TestsT{ + { + "test-ip-daddr", + exprs.NFT_FAMILY_IP, + "", + []*config.ExprValues{ + &config.ExprValues{ + Key: "daddr", + Value: "1.1.1.1", + }, + }, + 2, + []interface{}{ + &expr.Payload{ + SourceRegister: 0, + DestRegister: 1, + Offset: 16, + Base: expr.PayloadBaseNetworkHeader, + Len: 4, + }, + &expr.Cmp{ + Data: net.ParseIP("1.1.1.1").To4(), + }, + }, + false, + }, + { + "test-ip-saddr", + exprs.NFT_FAMILY_IP, + "", + []*config.ExprValues{ + &config.ExprValues{ + Key: "saddr", + Value: "1.1.1.1", + }, + }, + 2, + []interface{}{ + &expr.Payload{ + SourceRegister: 1, + DestRegister: 1, + Offset: 12, + Base: expr.PayloadBaseNetworkHeader, + Len: 4, + }, + &expr.Cmp{ + Data: net.ParseIP("1.1.1.1").To4(), + }, + }, + false, + }, + { + "test-inet-daddr", + exprs.NFT_FAMILY_INET, + "", + []*config.ExprValues{ + &config.ExprValues{ + Key: "daddr", + Value: "1.1.1.1", + }, + }, + 4, + []interface{}{ + &expr.Meta{ + Key: expr.MetaKeyNFPROTO, Register: 1, + }, + &expr.Cmp{ + Data: []byte{unix.NFPROTO_IPV4}, + }, + &expr.Payload{ + SourceRegister: 0, + DestRegister: 1, + Offset: 16, + Base: expr.PayloadBaseNetworkHeader, + Len: 4, + }, + &expr.Cmp{ + Data: net.ParseIP("1.1.1.1").To4(), + }, + }, + false, + }, + { + "test-ip-daddr-invalid", + exprs.NFT_FAMILY_IP, + "", + []*config.ExprValues{ + &config.ExprValues{ + Key: "daddr", + Value: "1.1.1", + }, + }, + 0, + []interface{}{}, + true, + }, + { + "test-ip-daddr-invalid", + exprs.NFT_FAMILY_IP, + "", + []*config.ExprValues{ + &config.ExprValues{ + Key: "daddr", + Value: "1..1.1.1", + }, + }, + 0, + []interface{}{}, + true, + }, + { + "test-ip-daddr-invalid", + exprs.NFT_FAMILY_IP, + "", + []*config.ExprValues{ + &config.ExprValues{ + Key: "daddr", + Value: "www.test.com", + }, + }, + 0, + []interface{}{}, + true, + }, + { + "test-ip-daddr-invalid", + exprs.NFT_FAMILY_IP, + "", + []*config.ExprValues{ + &config.ExprValues{ + Key: "daddr", + Value: "", + }, + }, + 0, + []interface{}{}, + true, + }, + { + "test-inet-saddr", + exprs.NFT_FAMILY_INET, + "", + []*config.ExprValues{ + &config.ExprValues{ + Key: "saddr", + Value: "1.1.1.1", + }, + }, + 4, + []interface{}{ + &expr.Meta{ + Key: expr.MetaKeyNFPROTO, Register: 1, + }, + &expr.Cmp{ + Data: []byte{unix.NFPROTO_IPV4}, + }, + &expr.Payload{ + SourceRegister: 1, + DestRegister: 1, + Offset: 12, + Base: expr.PayloadBaseNetworkHeader, + Len: 4, + }, + &expr.Cmp{ + Data: net.ParseIP("1.1.1.1").To4(), + }, + }, + false, + }, + { + "test-inet-daddr-invalid", + exprs.NFT_FAMILY_INET, + "", + []*config.ExprValues{ + &config.ExprValues{ + Key: "daddr", + Value: "1..1.1.1", + }, + }, + 0, + []interface{}{}, + true, + }, + { + "test-inet-saddr-invalid", + exprs.NFT_FAMILY_INET, + "", + []*config.ExprValues{ + &config.ExprValues{ + Key: "saddr", + Value: "1..1.1.1", + }, + }, + 0, + []interface{}{}, + true, + }, + { + "test-inet-range-daddr", + exprs.NFT_FAMILY_INET, + "", + []*config.ExprValues{ + &config.ExprValues{ + Key: "daddr", + Value: "1.1.1.1-2.2.2.2", + }, + }, + 4, + []interface{}{ + &expr.Meta{ + Key: expr.MetaKeyNFPROTO, Register: 1, + }, + &expr.Cmp{ + Data: []byte{unix.NFPROTO_IPV4}, + }, + &expr.Payload{ + SourceRegister: 0, + DestRegister: 1, + Offset: 16, + Base: expr.PayloadBaseNetworkHeader, + Len: 4, + }, + &expr.Range{ + Register: 1, + FromData: net.ParseIP("1.1.1.1").To4(), + ToData: net.ParseIP("2.2.2.2").To4(), + }, + }, + false, + }, + { + "test-inet-range-saddr", + exprs.NFT_FAMILY_INET, + "", + []*config.ExprValues{ + &config.ExprValues{ + Key: "saddr", + Value: "1.1.1.1-2.2.2.2", + }, + }, + 4, + []interface{}{ + &expr.Meta{ + Key: expr.MetaKeyNFPROTO, Register: 1, + }, + &expr.Cmp{ + Data: []byte{unix.NFPROTO_IPV4}, + }, + &expr.Payload{ + SourceRegister: 1, + DestRegister: 1, + Offset: 12, + Base: expr.PayloadBaseNetworkHeader, + Len: 4, + }, + &expr.Range{ + Register: 1, + FromData: net.ParseIP("1.1.1.1").To4(), + ToData: net.ParseIP("2.2.2.2").To4(), + }, + }, + false, + }, + { + "test-inet-daddr-range-invalid", + exprs.NFT_FAMILY_INET, + "", + []*config.ExprValues{ + &config.ExprValues{ + Key: "daddr", + Value: "1.1.1.1--2.2.2.2", + }, + }, + 0, + []interface{}{}, + true, + }, + { + "test-inet-daddr-range-invalid", + exprs.NFT_FAMILY_INET, + "", + []*config.ExprValues{ + &config.ExprValues{ + Key: "daddr", + Value: "1.1.1.1-1..2.2.2", + }, + }, + 0, + []interface{}{}, + true, + }, + { + "test-inet-daddr-range-invalid", + exprs.NFT_FAMILY_INET, + "", + []*config.ExprValues{ + &config.ExprValues{ + Key: "daddr", + // TODO: not supported yet + Value: "1.1.1.1/24", + }, + }, + 0, + []interface{}{}, + true, + }, + } + + for _, test := range tests { + t.Run(test.Name, func(t *testing.T) { + ipExpr, err := exprs.NewExprIP(test.Family, test.Values, expr.CmpOpEq) + if err != nil && !test.ExpectedFail { + t.Errorf("Error creating expr IP: %s", ipExpr) + return + } else if err != nil && test.ExpectedFail { + return + } + + r, _ := nftest.AddTestRule(t, conn, ipExpr) + if r == nil && !test.ExpectedFail { + t.Error("Error adding rule with IP expression") + } + + if !nftest.AreExprsValid(t, &test, r) { + return + } + + if test.ExpectedFail { + t.Errorf("test should have failed") + } + }) + } + +} diff --git a/daemon/firewall/nftables/exprs/limit.go b/daemon/firewall/nftables/exprs/limit.go new file mode 100644 index 0000000..0b1b4e1 --- /dev/null +++ b/daemon/firewall/nftables/exprs/limit.go @@ -0,0 +1,83 @@ +package exprs + +import ( + "fmt" + "strconv" + + "github.com/evilsocket/opensnitch/daemon/firewall/config" + "github.com/google/nftables/expr" +) + +// NewExprLimit returns a new limit expression. +// limit rate [over] 1/second +// to express bytes units, we use: 10-mbytes instead of nft's 10 mbytes +func NewExprLimit(statement *config.ExprStatement) (*[]expr.Any, error) { + var err error + exprLimit := &expr.Limit{ + Type: expr.LimitTypePkts, + Over: false, + Unit: expr.LimitTimeSecond, + } + + for _, values := range statement.Values { + switch values.Key { + + case NFT_LIMIT_OVER: + exprLimit.Over = true + + case NFT_LIMIT_UNITS: + exprLimit.Rate, err = strconv.ParseUint(values.Value, 10, 64) + if err != nil { + return nil, fmt.Errorf("Invalid limit rate: %s", values.Value) + } + + case NFT_LIMIT_BURST: + limitBurst := 0 + limitBurst, err = strconv.Atoi(values.Value) + if err != nil || limitBurst == 0 { + return nil, fmt.Errorf("Invalid burst limit: %s, err: %s", values.Value, err) + } + exprLimit.Burst = uint32(limitBurst) + + case NFT_LIMIT_UNITS_RATE: + // units rate must be placed AFTER the rate + exprLimit.Type, exprLimit.Rate = getLimitRate(values.Value, exprLimit.Rate) + + case NFT_LIMIT_UNITS_TIME: + exprLimit.Unit = getLimitUnits(values.Value) + } + } + + return &[]expr.Any{exprLimit}, nil +} + +func getLimitUnits(units string) (limitUnits expr.LimitTime) { + switch units { + case NFT_LIMIT_UNIT_MINUTE: + limitUnits = expr.LimitTimeMinute + case NFT_LIMIT_UNIT_HOUR: + limitUnits = expr.LimitTimeHour + case NFT_LIMIT_UNIT_DAY: + limitUnits = expr.LimitTimeDay + default: + limitUnits = expr.LimitTimeSecond + } + + return limitUnits +} + +func getLimitRate(units string, rate uint64) (limitType expr.LimitType, limitRate uint64) { + switch units { + case NFT_LIMIT_UNIT_KBYTES: + limitRate = rate * 1024 + limitType = expr.LimitTypePktBytes + case NFT_LIMIT_UNIT_MBYTES: + limitRate = (rate * 1024) * 1024 + limitType = expr.LimitTypePktBytes + default: + limitType = expr.LimitTypePkts + limitRate, _ = strconv.ParseUint(units, 10, 64) + } + + return +} diff --git a/daemon/firewall/nftables/exprs/log.go b/daemon/firewall/nftables/exprs/log.go new file mode 100644 index 0000000..10fdca2 --- /dev/null +++ b/daemon/firewall/nftables/exprs/log.go @@ -0,0 +1,73 @@ +package exprs + +import ( + "fmt" + + "github.com/evilsocket/opensnitch/daemon/firewall/config" + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/google/nftables/expr" + "golang.org/x/sys/unix" +) + +// NewExprLog returns a new log expression. +func NewExprLog(statement *config.ExprStatement) (*[]expr.Any, error) { + prefix := "opensnitch" + logExpr := expr.Log{ + Key: 1 << unix.NFTA_LOG_PREFIX, + Data: []byte(prefix), + } + + for _, values := range statement.Values { + switch values.Key { + case NFT_LOG_PREFIX: + if values.Value == "" { + return nil, fmt.Errorf("Invalid log prefix, it's empty") + } + logExpr.Data = []byte(values.Value) + case NFT_LOG_LEVEL: + lvl, err := getLogLevel(values.Value) + if err != nil { + log.Warning("%s", err) + return nil, err + } + logExpr.Key |= 1 << unix.NFTA_LOG_LEVEL + logExpr.Level = lvl + // TODO + // https://github.com/google/nftables/blob/main/nftables_test.go#L623 + //case exprs.NFT_LOG_FLAGS: + //case exprs.NFT_LOG_GROUP: + //case exprs.NFT_LOG_QTHRESHOLD: + } + } + + return &[]expr.Any{ + &logExpr, + }, nil + +} + +func getLogLevel(what string) (expr.LogLevel, error) { + switch what { + // https://github.com/google/nftables/blob/main/expr/log.go#L28 + case NFT_LOG_LEVEL_EMERG: + return expr.LogLevelEmerg, nil + case NFT_LOG_LEVEL_ALERT: + return expr.LogLevelAlert, nil + case NFT_LOG_LEVEL_CRIT: + return expr.LogLevelCrit, nil + case NFT_LOG_LEVEL_ERR: + return expr.LogLevelErr, nil + case NFT_LOG_LEVEL_WARN: + return expr.LogLevelWarning, nil + case NFT_LOG_LEVEL_NOTICE: + return expr.LogLevelNotice, nil + case NFT_LOG_LEVEL_INFO: + return expr.LogLevelInfo, nil + case NFT_LOG_LEVEL_DEBUG: + return expr.LogLevelDebug, nil + case NFT_LOG_LEVEL_AUDIT: + return expr.LogLevelAudit, nil + } + + return 0, fmt.Errorf("Invalid log level: %s", what) +} diff --git a/daemon/firewall/nftables/exprs/log_test.go b/daemon/firewall/nftables/exprs/log_test.go new file mode 100644 index 0000000..cab2d6f --- /dev/null +++ b/daemon/firewall/nftables/exprs/log_test.go @@ -0,0 +1,147 @@ +package exprs_test + +import ( + "testing" + + "github.com/evilsocket/opensnitch/daemon/firewall/config" + exprs "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/nftest" + "github.com/google/nftables/expr" + "golang.org/x/sys/unix" +) + +func TestExprLog(t *testing.T) { + nftest.SkipIfNotPrivileged(t) + + conn, newNS := nftest.OpenSystemConn(t) + defer nftest.CleanupSystemConn(t, newNS) + nftest.Fw.Conn = conn + + type logTestsT struct { + nftest.TestsT + statem *config.ExprStatement + } + tests := []logTestsT{ + { + TestsT: nftest.TestsT{ + Name: "test-log-prefix-simple", + Values: []*config.ExprValues{ + &config.ExprValues{ + Key: "prefix", + Value: "counter-test", + }, + }, + ExpectedExprs: []interface{}{ + &expr.Log{ + Key: 1 << unix.NFTA_LOG_PREFIX, + Data: []byte("counter-test"), + }, + }, + ExpectedExprsNum: 1, + ExpectedFail: false, + }, + statem: &config.ExprStatement{ + Op: "==", + Name: "log", + }, + }, + { + TestsT: nftest.TestsT{ + Name: "test-log-prefix-emerg", + Values: []*config.ExprValues{ + &config.ExprValues{ + Key: exprs.NFT_LOG_PREFIX, + Value: "counter-test-emerg", + }, + &config.ExprValues{ + Key: exprs.NFT_LOG_LEVEL, + Value: exprs.NFT_LOG_LEVEL_EMERG, + }, + }, + ExpectedExprs: []interface{}{ + &expr.Log{ + Key: (1 << unix.NFTA_LOG_PREFIX) | (1 << unix.NFTA_LOG_LEVEL), + Level: expr.LogLevelEmerg, + Data: []byte("counter-test-emerg"), + }, + }, + ExpectedExprsNum: 1, + ExpectedFail: false, + }, + statem: &config.ExprStatement{ + Op: "==", + Name: "log", + }, + }, + { + TestsT: nftest.TestsT{ + Name: "test-invalid-log-prefix", + Values: []*config.ExprValues{ + &config.ExprValues{ + Key: exprs.NFT_LOG_PREFIX, + Value: "", + }, + &config.ExprValues{ + Key: exprs.NFT_LOG_LEVEL, + Value: exprs.NFT_LOG_LEVEL_EMERG, + }, + }, + ExpectedExprs: []interface{}{}, + ExpectedExprsNum: 0, + ExpectedFail: true, + }, + statem: &config.ExprStatement{ + Op: "==", + Name: "log", + }, + }, + { + TestsT: nftest.TestsT{ + Name: "test-invalid-log-level", + Values: []*config.ExprValues{ + &config.ExprValues{ + Key: exprs.NFT_LOG_PREFIX, + Value: "counter-invalid-level", + }, + &config.ExprValues{ + Key: exprs.NFT_LOG_LEVEL, + Value: "", + }, + }, + ExpectedExprs: []interface{}{}, + ExpectedExprsNum: 0, + ExpectedFail: true, + }, + statem: &config.ExprStatement{ + Op: "==", + Name: "log", + }, + }, + } + + for _, test := range tests { + t.Run(test.Name, func(t *testing.T) { + + test.statem.Values = test.TestsT.Values + logExpr, err := exprs.NewExprLog(test.statem) + if err != nil && !test.ExpectedFail { + t.Errorf("Error creating expr Log: %s", logExpr) + return + } else if err != nil && test.ExpectedFail { + return + } + r, _ := nftest.AddTestRule(t, conn, logExpr) + if r == nil { + t.Error("Error adding rule with log expression") + } + + if !nftest.AreExprsValid(t, &test.TestsT, r) { + return + } + if test.ExpectedFail { + t.Errorf("test should have failed") + } + + }) + } +} diff --git a/daemon/firewall/nftables/exprs/meta.go b/daemon/firewall/nftables/exprs/meta.go new file mode 100644 index 0000000..682c4f9 --- /dev/null +++ b/daemon/firewall/nftables/exprs/meta.go @@ -0,0 +1,138 @@ +package exprs + +import ( + "fmt" + "strconv" + + "github.com/evilsocket/opensnitch/daemon/firewall/config" + "github.com/google/nftables/binaryutil" + "github.com/google/nftables/expr" +) + +// NewExprMeta creates a new meta selector to match or set packet metainformation. +// https://wiki.nftables.org/wiki-nftables/index.php/Matching_packet_metainformation +func NewExprMeta(values []*config.ExprValues, cmpOp *expr.CmpOp) (*[]expr.Any, error) { + setMark := false + metaExpr := []expr.Any{} + + for _, meta := range values { + switch meta.Key { + case NFT_META_SET_MARK: + setMark = true + continue + case NFT_META_MARK: + metaKey, err := getMetaKey(meta.Key) + if err != nil { + return nil, err + } + metaVal, err := getMetaValue(meta.Value) + if err != nil { + return nil, err + } + if setMark { + metaExpr = append(metaExpr, []expr.Any{ + &expr.Immediate{ + Register: 1, + Data: binaryutil.NativeEndian.PutUint32(uint32(metaVal)), + }}...) + metaExpr = append(metaExpr, []expr.Any{ + &expr.Meta{Key: metaKey, Register: 1, SourceRegister: setMark}}...) + } else { + metaExpr = append(metaExpr, []expr.Any{ + &expr.Meta{Key: metaKey, Register: 1, SourceRegister: setMark}, + &expr.Cmp{ + Op: *cmpOp, + Register: 1, + Data: binaryutil.NativeEndian.PutUint32(uint32(metaVal)), + }}...) + } + + setMark = false + return &metaExpr, nil + + case NFT_META_L4PROTO: + mexpr, err := NewExprProtocol(meta.Key) + if err != nil { + return nil, err + } + metaExpr = append(metaExpr, *mexpr...) + + return &metaExpr, nil + + case NFT_META_PRIORITY, + NFT_META_SKUID, NFT_META_SKGID, + NFT_META_PROTOCOL: + + metaKey, err := getMetaKey(meta.Key) + if err != nil { + return nil, err + } + metaVal, err := getProtocolCode(meta.Value) + if err != nil { + return nil, err + } + metaExpr = append(metaExpr, []expr.Any{ + &expr.Meta{Key: metaKey, Register: 1, SourceRegister: setMark}, + &expr.Cmp{ + Op: *cmpOp, + Register: 1, + Data: binaryutil.NativeEndian.PutUint32(uint32(metaVal)), + }}...) + + setMark = false + return &metaExpr, nil + + case NFT_META_NFTRACE: + mark, err := getMetaValue(meta.Value) + if err != nil { + return nil, err + } + if mark != 0 && mark != 1 { + return nil, fmt.Errorf("%s Invalid nftrace value: %d. Only 1 or 0 allowed", "nftables", mark) + } + // TODO: not working yet + return &[]expr.Any{ + &expr.Meta{Key: expr.MetaKeyNFTRACE, Register: 1}, + &expr.Cmp{ + Op: *cmpOp, + Register: 1, + Data: binaryutil.NativeEndian.PutUint32(uint32(mark)), + }, + }, nil + + default: + // not supported yet + } + } + + return nil, fmt.Errorf("%s meta keyword not supported yet, open a new issue on github", "nftables") +} + +func getMetaValue(value string) (int, error) { + metaVal, err := strconv.Atoi(value) + if err != nil { + return 0, err + } + return metaVal, nil +} + +// https://github.com/google/nftables/blob/main/expr/expr.go#L168 +func getMetaKey(value string) (expr.MetaKey, error) { + switch value { + case NFT_META_MARK: + return expr.MetaKeyMARK, nil + case NFT_META_PRIORITY: + return expr.MetaKeyPRIORITY, nil + case NFT_META_SKUID: + return expr.MetaKeySKUID, nil + case NFT_META_SKGID: + return expr.MetaKeySKGID, nil + // ip, ip6, arp, vlan + case NFT_META_PROTOCOL: + return expr.MetaKeyPROTOCOL, nil + case NFT_META_L4PROTO: + return expr.MetaKeyL4PROTO, nil + } + + return expr.MetaKeyPRANDOM, fmt.Errorf("meta key %s not supported (yet)", value) +} diff --git a/daemon/firewall/nftables/exprs/meta_test.go b/daemon/firewall/nftables/exprs/meta_test.go new file mode 100644 index 0000000..478278c --- /dev/null +++ b/daemon/firewall/nftables/exprs/meta_test.go @@ -0,0 +1,213 @@ +package exprs_test + +import ( + "testing" + + "github.com/evilsocket/opensnitch/daemon/firewall/config" + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/nftest" + "github.com/google/nftables/binaryutil" + "github.com/google/nftables/expr" +) + +func TestExprMeta(t *testing.T) { + nftest.SkipIfNotPrivileged(t) + + conn, newNS := nftest.OpenSystemConn(t) + defer nftest.CleanupSystemConn(t, newNS) + nftest.Fw.Conn = conn + + tests := []nftest.TestsT{ + { + "test-meta-mark", + exprs.NFT_FAMILY_IP, + "", + []*config.ExprValues{ + &config.ExprValues{ + Key: exprs.NFT_META_MARK, + Value: "666", + }, + }, + 2, + []interface{}{ + &expr.Meta{ + Key: expr.MetaKeyMARK, + Register: 1, + SourceRegister: false, + }, + &expr.Cmp{ + Data: binaryutil.NativeEndian.PutUint32(uint32(666)), + }, + }, + false, + }, + { + "test-meta-set-mark", + exprs.NFT_FAMILY_IP, + "", + []*config.ExprValues{ + &config.ExprValues{ + Key: exprs.NFT_META_SET_MARK, + Value: "", + }, + &config.ExprValues{ + Key: exprs.NFT_META_MARK, + Value: "666", + }, + }, + 2, + []interface{}{ + &expr.Immediate{ + Register: 1, + Data: binaryutil.NativeEndian.PutUint32(666), + }, + &expr.Meta{ + Key: expr.MetaKeyMARK, + Register: 1, + SourceRegister: true, + }, + }, + false, + }, + { + "test-meta-priority", + exprs.NFT_FAMILY_IP, + "", + []*config.ExprValues{ + &config.ExprValues{ + Key: exprs.NFT_META_PRIORITY, + Value: "1", + }, + }, + 2, + []interface{}{ + &expr.Meta{ + Key: expr.MetaKeyPRIORITY, + Register: 1, + SourceRegister: false, + }, + &expr.Cmp{ + Data: binaryutil.NativeEndian.PutUint32(uint32(1)), + }, + }, + false, + }, + { + "test-meta-skuid", + exprs.NFT_FAMILY_IP, + "", + []*config.ExprValues{ + &config.ExprValues{ + Key: exprs.NFT_META_SKUID, + Value: "1", + }, + }, + 2, + []interface{}{ + &expr.Meta{ + Key: expr.MetaKeySKUID, + Register: 1, + SourceRegister: false, + }, + &expr.Cmp{ + Data: binaryutil.NativeEndian.PutUint32(uint32(1)), + }, + }, + false, + }, + { + "test-meta-skgid", + exprs.NFT_FAMILY_IP, + "", + []*config.ExprValues{ + &config.ExprValues{ + Key: exprs.NFT_META_SKGID, + Value: "1", + }, + }, + 2, + []interface{}{ + &expr.Meta{ + Key: expr.MetaKeySKGID, + Register: 1, + SourceRegister: false, + }, + &expr.Cmp{ + Data: binaryutil.NativeEndian.PutUint32(uint32(1)), + }, + }, + false, + }, + { + "test-meta-protocol", + exprs.NFT_FAMILY_IP, + "", + []*config.ExprValues{ + &config.ExprValues{ + Key: exprs.NFT_META_PROTOCOL, + Value: "15", + }, + }, + 2, + []interface{}{ + &expr.Meta{ + Key: expr.MetaKeyPROTOCOL, + Register: 1, + SourceRegister: false, + }, + &expr.Cmp{ + Data: binaryutil.NativeEndian.PutUint32(uint32(15)), + }, + }, + false, + }, + // tested more in depth in protocol_test.go + { + "test-meta-l4proto", + exprs.NFT_FAMILY_IP, + "", + []*config.ExprValues{ + &config.ExprValues{ + Key: exprs.NFT_META_L4PROTO, + Value: "15", + }, + }, + 1, + []interface{}{ + &expr.Meta{ + Key: expr.MetaKeyL4PROTO, + Register: 1, + SourceRegister: false, + }, + }, + false, + }, + } + + for _, test := range tests { + t.Run(test.Name, func(t *testing.T) { + cmp := expr.CmpOpEq + metaExpr, err := exprs.NewExprMeta(test.Values, &cmp) + if err != nil && !test.ExpectedFail { + t.Errorf("Error creating expr Meta: %s", metaExpr) + return + } else if err != nil && test.ExpectedFail { + return + } + + r, _ := nftest.AddTestRule(t, conn, metaExpr) + if r == nil && !test.ExpectedFail { + t.Error("Error adding rule with Meta expression") + } + + if !nftest.AreExprsValid(t, &test, r) { + return + } + + if test.ExpectedFail { + t.Errorf("test should have failed") + } + }) + } + +} diff --git a/daemon/firewall/nftables/exprs/nat.go b/daemon/firewall/nftables/exprs/nat.go new file mode 100644 index 0000000..207eb38 --- /dev/null +++ b/daemon/firewall/nftables/exprs/nat.go @@ -0,0 +1,152 @@ +package exprs + +import ( + "fmt" + "net" + "strconv" + "strings" + + "github.com/google/nftables" + "github.com/google/nftables/binaryutil" + "github.com/google/nftables/expr" + "golang.org/x/sys/unix" +) + +// NewExprNATFlags returns the nat flags configured. +// common to masquerade, snat and dnat +func NewExprNATFlags(parms string) (random, fullrandom, persistent bool) { + masqParms := strings.Split(parms, ",") + for _, mParm := range masqParms { + switch mParm { + case NFT_MASQ_RANDOM: + random = true + case NFT_MASQ_FULLY_RANDOM: + fullrandom = true + case NFT_MASQ_PERSISTENT: + persistent = true + } + } + + return +} + +// NewExprNAT parses the redirection of redirect, snat, dnat, tproxy and masquerade verdict: +// to x.y.z.a:abcd +// If only the IP is specified (to 1.2.3.4), only NAT.RegAddrMin must be present (regAddr == true) +// If only the port is specified (to :1234), only NAT.RegPortMin must be present (regPort == true) +// If both addr and port are specified (to 1.2.3.4:1234), NAT.RegPortMin and NAT.RegAddrMin must be present. +func NewExprNAT(parms, verdict string) (bool, bool, *[]expr.Any, error) { + regAddr := false + regProto := false + exprNAT := []expr.Any{} + NATParms := strings.Split(parms, " ") + + idx := 0 + // exclude first parameter if it's "to" + if NATParms[idx] == NFT_PARM_TO { + idx++ + } + if idx == len(NATParms) { + return regAddr, regProto, &exprNAT, fmt.Errorf("Invalid parms: %s", parms) + } + + dParms := strings.Split(NATParms[idx], ":") + // masquerade doesn't allow "to IP" + if dParms[0] != "" && verdict != VERDICT_MASQUERADE { + dIP := dParms[0] + destIP := net.ParseIP(dIP) + if destIP == nil { + return regAddr, regProto, &exprNAT, fmt.Errorf("Invalid IP: %s", dIP) + } + + exprNAT = append(exprNAT, []expr.Any{ + &expr.Immediate{ + Register: 1, + Data: destIP.To4(), + }}...) + regAddr = true + } + + if len(dParms) == 2 { + dPort := dParms[1] + // TODO: support ranges. 9000-9100 + destPort, err := strconv.Atoi(dPort) + if err != nil { + return regAddr, regProto, &exprNAT, fmt.Errorf("Invalid Port: %s", dPort) + } + reg := uint32(2) + toPort := binaryutil.BigEndian.PutUint16(uint16(destPort)) + // if reg=1 (RegAddrMin=1) is not set, this error appears listing the rules + // "netlink: Error: NAT statement has no proto expression" + if verdict == VERDICT_TPROXY || verdict == VERDICT_MASQUERADE || verdict == VERDICT_REDIRECT { + // according to https://github.com/google/nftables/blob/8a10f689006bf728a5cff35787713047f68e308a/nftables_test.go#L4871 + // Masquerade ports should be specified like this: + // toPort = binaryutil.BigEndian.PutUint32(uint32(destPort) << 16) + // but then it's not added/listed correctly with nft. + + reg = 1 + } + exprNAT = append(exprNAT, []expr.Any{ + &expr.Immediate{ + Register: reg, + Data: toPort, + }}...) + regProto = true + } + + return regAddr, regProto, &exprNAT, nil +} + +// NewExprMasquerade returns a new masquerade expression. +func NewExprMasquerade(toPorts, random, fullRandom, persistent bool) *[]expr.Any { + exprMasq := &expr.Masq{ + ToPorts: toPorts, + Random: random, + FullyRandom: fullRandom, + Persistent: persistent, + } + if toPorts { + exprMasq.RegProtoMin = 1 + } + return &[]expr.Any{ + exprMasq, + } +} + +// NewExprRedirect returns a new redirect expression. +func NewExprRedirect() *[]expr.Any { + return &[]expr.Any{ + // Redirect is a special case of DNAT where the destination is the current machine + &expr.Redir{ + RegisterProtoMin: 1, + }, + } +} + +// NewExprSNAT returns a new snat expression. +func NewExprSNAT() *expr.NAT { + return &expr.NAT{ + Type: expr.NATTypeSourceNAT, + Family: unix.NFPROTO_IPV4, + } +} + +// NewExprDNAT returns a new dnat expression. +func NewExprDNAT() *expr.NAT { + return &expr.NAT{ + Type: expr.NATTypeDestNAT, + Family: unix.NFPROTO_IPV4, + } +} + +// NewExprTproxy returns a new tproxy expression. +// XXX: is "to x.x.x.x:1234" supported by google/nftables lib? or only "to :1234"? +// it creates an erronous rule. +func NewExprTproxy() *[]expr.Any { + return &[]expr.Any{ + &expr.TProxy{ + Family: byte(nftables.TableFamilyIPv4), + TableFamily: byte(nftables.TableFamilyIPv4), + RegPort: 1, + }} +} diff --git a/daemon/firewall/nftables/exprs/nat_test.go b/daemon/firewall/nftables/exprs/nat_test.go new file mode 100644 index 0000000..ec8ce67 --- /dev/null +++ b/daemon/firewall/nftables/exprs/nat_test.go @@ -0,0 +1,627 @@ +package exprs_test + +import ( + "net" + "testing" + + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/nftest" + "github.com/google/nftables" + "github.com/google/nftables/binaryutil" + "github.com/google/nftables/expr" + "golang.org/x/sys/unix" +) + +func TestExprVerdictSNAT(t *testing.T) { + nftest.SkipIfNotPrivileged(t) + + conn, newNS := nftest.OpenSystemConn(t) + defer nftest.CleanupSystemConn(t, newNS) + nftest.Fw.Conn = conn + + // TODO: test random, permanent, persistent flags. + tests := []nftest.TestsT{ + { + "test-nat-snat-to-127001", + exprs.NFT_FAMILY_IP, + "to 127.0.0.1", + nil, + 2, + []interface{}{ + &expr.Immediate{ + Register: 1, + Data: net.ParseIP("127.0.0.1").To4(), + }, + &expr.NAT{ + Type: expr.NATTypeSourceNAT, + Family: unix.NFPROTO_IPV4, + Random: false, + FullyRandom: false, + Persistent: false, + RegAddrMin: 1, + }, + }, + false, + }, + { + "test-nat-snat-127001", + exprs.NFT_FAMILY_IP, + "127.0.0.1", + nil, + 2, + []interface{}{ + &expr.Immediate{ + Register: 1, + Data: net.ParseIP("127.0.0.1").To4(), + }, + &expr.NAT{ + Type: expr.NATTypeSourceNAT, + Family: unix.NFPROTO_IPV4, + Random: false, + FullyRandom: false, + Persistent: false, + RegAddrMin: 1, + }, + }, + false, + }, + { + "test-nat-snat-to-127001:12345", + exprs.NFT_FAMILY_IP, + "to 127.0.0.1:12345", + nil, + 3, + []interface{}{ + &expr.Immediate{ + Register: 1, + Data: net.ParseIP("127.0.0.1").To4(), + }, + &expr.Immediate{ + Register: uint32(2), + Data: binaryutil.BigEndian.PutUint16(uint16(12345)), + }, + &expr.NAT{ + Type: expr.NATTypeSourceNAT, + Family: unix.NFPROTO_IPV4, + Random: false, + FullyRandom: false, + Persistent: false, + RegAddrMin: 1, + RegProtoMin: 2, + }, + }, + false, + }, + { + "test-nat-snat-to-:12345", + exprs.NFT_FAMILY_IP, + "to :12345", + nil, + 2, + []interface{}{ + &expr.Immediate{ + Register: uint32(2), + Data: binaryutil.BigEndian.PutUint16(uint16(12345)), + }, + &expr.NAT{ + Type: expr.NATTypeSourceNAT, + Family: unix.NFPROTO_IPV4, + Random: false, + FullyRandom: false, + Persistent: false, + RegAddrMin: 0, + RegProtoMin: 2, + }, + }, + false, + }, + { + "test-nat-snat-127001:12345", + exprs.NFT_FAMILY_IP, + "127.0.0.1:12345", + nil, + 3, + []interface{}{ + &expr.Immediate{ + Register: 1, + Data: net.ParseIP("127.0.0.1").To4(), + }, + &expr.Immediate{ + Register: uint32(2), + Data: binaryutil.BigEndian.PutUint16(uint16(12345)), + }, + &expr.NAT{ + Type: expr.NATTypeSourceNAT, + Family: unix.NFPROTO_IPV4, + Random: false, + FullyRandom: false, + Persistent: false, + RegAddrMin: 1, + RegProtoMin: 2, + }, + }, + false, + }, + { + "test-invalid-nat-snat-to-", + exprs.NFT_FAMILY_IP, + "to", + nil, + 3, + []interface{}{}, + true, + }, + { + "test-invalid-nat-snat-to-invalid-ip", + exprs.NFT_FAMILY_IP, + "to 127..0.0.1", + nil, + 3, + []interface{}{}, + true, + }, + { + "test-invalid-nat-snat-to-invalid-port", + exprs.NFT_FAMILY_IP, + "to 127.0.0.1:aaa", + nil, + 3, + []interface{}{}, + true, + }, + } + for _, test := range tests { + t.Run(test.Name, func(t *testing.T) { + + verdExpr := exprs.NewExprVerdict(exprs.VERDICT_SNAT, test.Parms) + if !test.ExpectedFail && verdExpr == nil { + t.Errorf("error creating snat verdict") + } else if test.ExpectedFail && verdExpr == nil { + return + } + r, _ := nftest.AddTestSNATRule(t, conn, verdExpr) + if r == nil { + t.Errorf("Error adding rule") + return + } + + if !nftest.AreExprsValid(t, &test, r) { + return + } + + if test.ExpectedFail { + t.Errorf("test should have failed") + } + + }) + } +} + +func TestExprVerdictDNAT(t *testing.T) { + nftest.SkipIfNotPrivileged(t) + + conn, newNS := nftest.OpenSystemConn(t) + defer nftest.CleanupSystemConn(t, newNS) + nftest.Fw.Conn = conn + + tests := []nftest.TestsT{ + { + "test-nat-dnat-to-127001", + exprs.NFT_FAMILY_IP, + "to 127.0.0.1", + nil, + 2, + []interface{}{ + &expr.Immediate{ + Register: 1, + Data: net.ParseIP("127.0.0.1").To4(), + }, + &expr.NAT{ + Type: expr.NATTypeDestNAT, + Family: unix.NFPROTO_IPV4, + Random: false, + FullyRandom: false, + Persistent: false, + RegAddrMin: 1, + }, + }, + false, + }, + { + "test-nat-dnat-127001", + exprs.NFT_FAMILY_IP, + "127.0.0.1", + nil, + 2, + []interface{}{ + &expr.Immediate{ + Register: 1, + Data: net.ParseIP("127.0.0.1").To4(), + }, + &expr.NAT{ + Type: expr.NATTypeDestNAT, + Family: unix.NFPROTO_IPV4, + Random: false, + FullyRandom: false, + Persistent: false, + RegAddrMin: 1, + }, + }, + false, + }, + { + "test-nat-dnat-to-127001:12345", + exprs.NFT_FAMILY_IP, + "to 127.0.0.1:12345", + nil, + 3, + []interface{}{ + &expr.Immediate{ + Register: 1, + Data: net.ParseIP("127.0.0.1").To4(), + }, + &expr.Immediate{ + Register: uint32(2), + Data: binaryutil.BigEndian.PutUint16(uint16(12345)), + }, + &expr.NAT{ + Type: expr.NATTypeDestNAT, + Family: unix.NFPROTO_IPV4, + Random: false, + FullyRandom: false, + Persistent: false, + RegAddrMin: 1, + RegProtoMin: 2, + }, + }, + false, + }, + { + "test-nat-dnat-to-:12345", + exprs.NFT_FAMILY_IP, + "to :12345", + nil, + 2, + []interface{}{ + &expr.Immediate{ + Register: uint32(2), + Data: binaryutil.BigEndian.PutUint16(uint16(12345)), + }, + &expr.NAT{ + Type: expr.NATTypeDestNAT, + Family: unix.NFPROTO_IPV4, + Random: false, + FullyRandom: false, + Persistent: false, + RegAddrMin: 0, + RegProtoMin: 2, + }, + }, + false, + }, + { + "test-nat-dnat-127001:12345", + exprs.NFT_FAMILY_IP, + "127.0.0.1:12345", + nil, + 3, + []interface{}{ + &expr.Immediate{ + Register: 1, + Data: net.ParseIP("127.0.0.1").To4(), + }, + &expr.Immediate{ + Register: uint32(2), + Data: binaryutil.BigEndian.PutUint16(uint16(12345)), + }, + &expr.NAT{ + Type: expr.NATTypeDestNAT, + Family: unix.NFPROTO_IPV4, + Random: false, + FullyRandom: false, + Persistent: false, + RegAddrMin: 1, + RegProtoMin: 2, + }, + }, + false, + }, + { + "test-invalid-nat-dnat-to-", + exprs.NFT_FAMILY_IP, + "to", + nil, + 3, + []interface{}{}, + true, + }, + { + "test-invalid-nat-dnat-to-invalid-ip", + exprs.NFT_FAMILY_IP, + "to 127..0.0.1", + nil, + 3, + []interface{}{}, + true, + }, + { + "test-invalid-nat-dnat-to-invalid-port", + exprs.NFT_FAMILY_IP, + "to 127.0.0.1:aaa", + nil, + 3, + []interface{}{}, + true, + }, + } + + for _, test := range tests { + t.Run(test.Name, func(t *testing.T) { + + verdExpr := exprs.NewExprVerdict(exprs.VERDICT_DNAT, test.Parms) + if !test.ExpectedFail && verdExpr == nil { + t.Errorf("error creating verdict") + } else if test.ExpectedFail && verdExpr == nil { + return + } + r, _ := nftest.AddTestDNATRule(t, conn, verdExpr) + if r == nil { + t.Errorf("Error adding rule") + return + } + + if !nftest.AreExprsValid(t, &test, r) { + return + } + + if test.ExpectedFail { + t.Errorf("test should have failed") + } + + }) + } +} + +func TestExprVerdictMasquerade(t *testing.T) { + nftest.SkipIfNotPrivileged(t) + + conn, newNS := nftest.OpenSystemConn(t) + defer nftest.CleanupSystemConn(t, newNS) + nftest.Fw.Conn = conn + + tests := []nftest.TestsT{ + { + "test-nat-masq-to-:12345", + exprs.NFT_FAMILY_IP, + "to :12345", + nil, + 2, + []interface{}{ + &expr.Immediate{ + Register: uint32(1), + Data: binaryutil.BigEndian.PutUint16(uint16(12345)), + }, + &expr.Masq{ + ToPorts: true, + Random: false, + FullyRandom: false, + Persistent: false, + }, + }, + false, + }, + { + "test-nat-masq-flags", + exprs.NFT_FAMILY_IP, + "random,fully-random,persistent", + nil, + 1, + []interface{}{ + &expr.Masq{ + ToPorts: false, + Random: true, + FullyRandom: true, + Persistent: true, + }, + }, + false, + }, + { + "test-nat-masq-empty", + exprs.NFT_FAMILY_IP, + "", + nil, + 1, + []interface{}{ + &expr.Masq{}, + }, + false, + }, + } + + for _, test := range tests { + t.Run(test.Name, func(t *testing.T) { + + verdExpr := exprs.NewExprVerdict(exprs.VERDICT_MASQUERADE, test.Parms) + if !test.ExpectedFail && verdExpr == nil { + t.Errorf("error creating verdict") + } else if test.ExpectedFail && verdExpr == nil { + return + } + r, _ := nftest.AddTestSNATRule(t, conn, verdExpr) + if r == nil { + t.Errorf("Error adding rule") + return + } + + if !nftest.AreExprsValid(t, &test, r) { + return + } + + if test.ExpectedFail { + t.Errorf("test should have failed") + } + + }) + } +} + +func TestExprVerdictRedirect(t *testing.T) { + nftest.SkipIfNotPrivileged(t) + + conn, newNS := nftest.OpenSystemConn(t) + defer nftest.CleanupSystemConn(t, newNS) + nftest.Fw.Conn = conn + + tests := []nftest.TestsT{ + { + "test-nat-redir-to-127001:12345", + exprs.NFT_FAMILY_IP, + "to 127.0.0.1:12345", + nil, + 3, + []interface{}{ + &expr.Immediate{ + Register: 1, + Data: net.ParseIP("127.0.0.1").To4(), + }, + &expr.Immediate{ + Register: uint32(1), + Data: binaryutil.BigEndian.PutUint16(uint16(12345)), + }, + &expr.Redir{ + RegisterProtoMin: 1, + }, + }, + false, + }, + { + "test-nat-redir-to-:12345", + exprs.NFT_FAMILY_IP, + "to :12345", + nil, + 2, + []interface{}{ + &expr.Immediate{ + Register: uint32(1), + Data: binaryutil.BigEndian.PutUint16(uint16(12345)), + }, + &expr.Redir{ + RegisterProtoMin: 1, + }, + }, + false, + }, + } + + for _, test := range tests { + t.Run(test.Name, func(t *testing.T) { + + verdExpr := exprs.NewExprVerdict(exprs.VERDICT_REDIRECT, test.Parms) + if !test.ExpectedFail && verdExpr == nil { + t.Errorf("error creating verdict") + } else if test.ExpectedFail && verdExpr == nil { + return + } + r, _ := nftest.AddTestDNATRule(t, conn, verdExpr) + if r == nil { + t.Errorf("Error adding rule") + return + } + + if !nftest.AreExprsValid(t, &test, r) { + return + } + + if test.ExpectedFail { + t.Errorf("test should have failed") + } + + }) + } +} + +func TestExprVerdictTProxy(t *testing.T) { + nftest.SkipIfNotPrivileged(t) + + conn, newNS := nftest.OpenSystemConn(t) + defer nftest.CleanupSystemConn(t, newNS) + nftest.Fw.Conn = conn + + tests := []nftest.TestsT{ + { + "test-nat-tproxy-to-127001:12345", + exprs.NFT_FAMILY_IP, + "to 127.0.0.1:12345", + nil, + 4, + []interface{}{ + &expr.Immediate{ + Register: 1, + Data: net.ParseIP("127.0.0.1").To4(), + }, + &expr.Immediate{ + Register: uint32(1), + Data: binaryutil.BigEndian.PutUint16(uint16(12345)), + }, + &expr.TProxy{ + Family: byte(nftables.TableFamilyIPv4), + TableFamily: byte(nftables.TableFamilyIPv4), + RegPort: 1, + }, + &expr.Verdict{ + Kind: expr.VerdictAccept, + }, + }, + false, + }, + { + "test-nat-tproxy-to-:12345", + exprs.NFT_FAMILY_IP, + "to :12345", + nil, + 3, + []interface{}{ + &expr.Immediate{ + Register: uint32(1), + Data: binaryutil.BigEndian.PutUint16(uint16(12345)), + }, + &expr.TProxy{ + Family: byte(nftables.TableFamilyIPv4), + TableFamily: byte(nftables.TableFamilyIPv4), + RegPort: 1, + }, + &expr.Verdict{ + Kind: expr.VerdictAccept, + }, + }, + false, + }, + } + + for _, test := range tests { + t.Run(test.Name, func(t *testing.T) { + + verdExpr := exprs.NewExprVerdict(exprs.VERDICT_TPROXY, test.Parms) + if !test.ExpectedFail && verdExpr == nil { + t.Errorf("error creating verdict") + } else if test.ExpectedFail && verdExpr == nil { + return + } + r, _ := nftest.AddTestDNATRule(t, conn, verdExpr) + if r == nil { + t.Errorf("Error adding rule") + return + } + + if !nftest.AreExprsValid(t, &test, r) { + return + } + + if test.ExpectedFail { + t.Errorf("test should have failed") + } + + }) + } +} diff --git a/daemon/firewall/nftables/exprs/notrack.go b/daemon/firewall/nftables/exprs/notrack.go new file mode 100644 index 0000000..aa68bdd --- /dev/null +++ b/daemon/firewall/nftables/exprs/notrack.go @@ -0,0 +1,10 @@ +package exprs + +import "github.com/google/nftables/expr" + +// NewNoTrack adds a new expression not to track connections. +func NewNoTrack() *[]expr.Any { + return &[]expr.Any{ + &expr.Notrack{}, + } +} diff --git a/daemon/firewall/nftables/exprs/operator.go b/daemon/firewall/nftables/exprs/operator.go new file mode 100644 index 0000000..3fc1e6d --- /dev/null +++ b/daemon/firewall/nftables/exprs/operator.go @@ -0,0 +1,33 @@ +package exprs + +import ( + "github.com/google/nftables/expr" +) + +// NewOperator translates a string comparator operator to nftables operator +func NewOperator(operator string) expr.CmpOp { + switch operator { + case "!=": + return expr.CmpOpNeq + case ">": + return expr.CmpOpGt + case ">=": + return expr.CmpOpGte + case "<": + return expr.CmpOpLt + case "<=": + return expr.CmpOpLte + } + + return expr.CmpOpEq +} + +// NewExprOperator returns a new comparator operator +func NewExprOperator(op expr.CmpOp) *[]expr.Any { + return &[]expr.Any{ + &expr.Cmp{ + Register: 1, + Op: op, + }, + } +} diff --git a/daemon/firewall/nftables/exprs/port.go b/daemon/firewall/nftables/exprs/port.go new file mode 100644 index 0000000..220572a --- /dev/null +++ b/daemon/firewall/nftables/exprs/port.go @@ -0,0 +1,97 @@ +package exprs + +import ( + "fmt" + "strconv" + "strings" + + "github.com/google/nftables" + "github.com/google/nftables/binaryutil" + "github.com/google/nftables/expr" +) + +// NewExprPort returns a new port expression with the given matching operator. +func NewExprPort(port string, op *expr.CmpOp) (*[]expr.Any, error) { + eport, err := strconv.Atoi(port) + if err != nil { + return nil, err + } + return &[]expr.Any{ + &expr.Cmp{ + Register: 1, + Op: *op, + Data: binaryutil.BigEndian.PutUint16(uint16(eport))}, + }, nil +} + +// NewExprPortRange returns a new port range expression. +func NewExprPortRange(sport string, cmpOp *expr.CmpOp) (*[]expr.Any, error) { + ports := strings.Split(sport, "-") + iport, err := strconv.Atoi(ports[0]) + if err != nil { + return nil, err + } + eport, err := strconv.Atoi(ports[1]) + if err != nil { + return nil, err + } + return &[]expr.Any{ + &expr.Range{ + Op: *cmpOp, + Register: 1, + FromData: binaryutil.BigEndian.PutUint16(uint16(iport)), + ToData: binaryutil.BigEndian.PutUint16(uint16(eport)), + }, + }, nil + +} + +// NewExprPortSet returns a new set of ports. +func NewExprPortSet(portv string) *[]nftables.SetElement { + setElements := []nftables.SetElement{} + ports := strings.Split(portv, ",") + for _, portv := range ports { + portExpr := exprPortSubSet(portv) + if portExpr != nil { + setElements = append(setElements, *portExpr...) + } + } + + return &setElements +} + +func exprPortSubSet(portv string) *[]nftables.SetElement { + port, err := strconv.Atoi(portv) + if err != nil { + return nil + } + + return &[]nftables.SetElement{ + {Key: binaryutil.BigEndian.PutUint16(uint16(port))}, + } + +} + +// NewExprPortDirection returns a new expression to match connections based on +// the direction of the connection (source, dest) +func NewExprPortDirection(direction string) (*expr.Payload, error) { + switch direction { + case NFT_DPORT: + return &expr.Payload{ + DestRegister: 1, + Base: expr.PayloadBaseTransportHeader, + Offset: 2, + Len: 2, + }, nil + case NFT_SPORT: + return &expr.Payload{ + DestRegister: 1, + Base: expr.PayloadBaseTransportHeader, + Offset: 0, + Len: 2, + }, nil + default: + return nil, fmt.Errorf("Not valid protocol direction: %s", direction) + } + +} diff --git a/daemon/firewall/nftables/exprs/port_test.go b/daemon/firewall/nftables/exprs/port_test.go new file mode 100644 index 0000000..553a140 --- /dev/null +++ b/daemon/firewall/nftables/exprs/port_test.go @@ -0,0 +1,114 @@ +package exprs_test + +import ( + "bytes" + "fmt" + "reflect" + "testing" + + exprs "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/nftest" + "github.com/google/nftables/binaryutil" + "github.com/google/nftables/expr" +) + +type portTestsT struct { + port string + portVal int + cmp expr.CmpOp + shouldFail bool +} + +func TestExprPort(t *testing.T) { + nftest.SkipIfNotPrivileged(t) + + conn, newNS := nftest.OpenSystemConn(t) + defer nftest.CleanupSystemConn(t, newNS) + nftest.Fw.Conn = conn + + portTests := []portTestsT{ + {"53", 53, expr.CmpOpEq, false}, + {"80", 80, expr.CmpOpEq, false}, + {"65535", 65535, expr.CmpOpEq, false}, + {"45,", 0, expr.CmpOpEq, true}, + {"", 0, expr.CmpOpEq, true}, + } + + for _, test := range portTests { + t.Run(fmt.Sprint("test-", test.port), func(t *testing.T) { + portExpr, err := exprs.NewExprPort(test.port, &test.cmp) + if err != nil { + if !test.shouldFail { + t.Errorf("Error creating expr port: %v, %s", test, err) + } + return + } + //fmt.Printf("%s, %+v\n", test.port, *portExpr) + r, _ := nftest.AddTestRule(t, conn, portExpr) + if r == nil { + t.Errorf("Error adding rule with port (%s) expression", test.port) + } + e := r.Exprs[0] + cmp, ok := e.(*expr.Cmp) + if !ok { + t.Errorf("%s - invalid port expr: %T", test.port, e) + } + //fmt.Printf("%s, %+v\n", reflect.TypeOf(e).String(), e) + if reflect.TypeOf(e).String() != "*expr.Cmp" { + t.Errorf("%s - first expression should be *expr.Cmp, instead of: %s", test.port, reflect.TypeOf(e)) + } + portVal := binaryutil.BigEndian.PutUint16(uint16(test.portVal)) + if !bytes.Equal(cmp.Data, portVal) { + t.Errorf("%s - invalid port in expr.Cmp: %d", test.port, cmp.Data) + } + + }) + } +} + +func TestExprPortRange(t *testing.T) { + nftest.SkipIfNotPrivileged(t) + + conn, newNS := nftest.OpenSystemConn(t) + defer nftest.CleanupSystemConn(t, newNS) + nftest.Fw.Conn = conn + + portTests := []portTestsT{ + {"53-5353", 53, expr.CmpOpEq, false}, + {"80-8080", 80, expr.CmpOpEq, false}, + {"1-65535", 65535, expr.CmpOpEq, false}, + {"1,45,", 0, expr.CmpOpEq, true}, + {"1-2.", 0, expr.CmpOpEq, true}, + } + + for _, test := range portTests { + t.Run(fmt.Sprint("test-", test.port), func(t *testing.T) { + portExpr, err := exprs.NewExprPortRange(test.port, &test.cmp) + if err != nil { + if !test.shouldFail { + t.Errorf("Error creating expr port range: %v, %s", test, err) + } + return + } + //fmt.Printf("%s, %+v\n", test.port, *portExpr) + r, _ := nftest.AddTestRule(t, conn, portExpr) + if r == nil { + t.Errorf("Error adding rule with port range (%s) expression", test.port) + } + e := r.Exprs[0] + _, ok := e.(*expr.Range) + if !ok { + t.Errorf("%s - invalid port range expr: %T", test.port, e) + } + fmt.Printf("%s, %+v\n", reflect.TypeOf(e).String(), e) + if reflect.TypeOf(e).String() != "*expr.Range" { + t.Errorf("%s - first expression should be *expr.Cmp, instead of: %s", test.port, reflect.TypeOf(e)) + } + /*portVal := binaryutil.BigEndian.PutUint16(uint16(test.portVal)) + if !bytes.Equal(range.FromData, portVal) { + t.Errorf("%s - invalid port range in expr.Cmp: %d", test.port, cmp.Data) + }*/ + + }) + } +} diff --git a/daemon/firewall/nftables/exprs/protocol.go b/daemon/firewall/nftables/exprs/protocol.go new file mode 100644 index 0000000..ac039d4 --- /dev/null +++ b/daemon/firewall/nftables/exprs/protocol.go @@ -0,0 +1,143 @@ +package exprs + +import ( + "fmt" + "strings" + + "github.com/google/nftables" + "github.com/google/nftables/expr" + "golang.org/x/sys/unix" +) + +// NewExprProtocol creates a new expression to filter connections by protocol +func NewExprProtocol(proto string) (*[]expr.Any, error) { + protoExpr := expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1} + + switch strings.ToLower(proto) { + case NFT_META_L4PROTO: + return &[]expr.Any{ + &protoExpr, + }, nil + + case NFT_PROTO_UDP: + return &[]expr.Any{ + &protoExpr, + &expr.Cmp{ + Op: expr.CmpOpEq, + Register: 1, + Data: []byte{unix.IPPROTO_UDP}, + }, + }, nil + + case NFT_PROTO_TCP: + return &[]expr.Any{ + &protoExpr, + &expr.Cmp{ + Op: expr.CmpOpEq, + Register: 1, + Data: []byte{unix.IPPROTO_TCP}, + }, + }, nil + + case NFT_PROTO_UDPLITE: + return &[]expr.Any{ + &protoExpr, + &expr.Cmp{ + Op: expr.CmpOpEq, + Register: 1, + Data: []byte{unix.IPPROTO_UDPLITE}, + }, + }, nil + + case NFT_PROTO_SCTP: + return &[]expr.Any{ + &protoExpr, + &expr.Cmp{ + Op: expr.CmpOpEq, + Register: 1, + Data: []byte{unix.IPPROTO_SCTP}, + }, + }, nil + + case NFT_PROTO_DCCP: + return &[]expr.Any{ + &protoExpr, + &expr.Cmp{ + Op: expr.CmpOpEq, + Register: 1, + Data: []byte{unix.IPPROTO_DCCP}, + }, + }, nil + + case NFT_PROTO_ICMP: + return &[]expr.Any{ + &protoExpr, + &expr.Cmp{ + Op: expr.CmpOpEq, + Register: 1, + Data: []byte{unix.IPPROTO_ICMP}, + }, + }, nil + + case NFT_PROTO_ICMPv6: + return &[]expr.Any{ + &protoExpr, + &expr.Cmp{ + Op: expr.CmpOpEq, + Register: 1, + Data: []byte{unix.IPPROTO_ICMPV6}, + }, + }, nil + + /*TODO: could be simplified + default: + proto, err := getProtocolCode(value) + if err != nil { + return nil, err + } + return &[]expr.Any{ + protoExpr, + &expr.Cmp{ + Op: expr.CmpOpEq, + Register: 1, + Data: []byte{byte(proto)}, + }, + }, nil + */ + default: + return nil, fmt.Errorf("Not valid protocol rule, invalid or not supported protocol: %s", proto) + } + +} + +// NewExprProtoSet creates a new list of SetElements{}, to match +// multiple protocol values. +func NewExprProtoSet(l4prots string) *[]nftables.SetElement { + protoList := strings.Split(l4prots, ",") + protoSet := []nftables.SetElement{} + for _, name := range protoList { + pcode, err := getProtocolCode(name) + if err != nil { + continue + } + + protoSet = append(protoSet, + []nftables.SetElement{ + {Key: []byte{byte(pcode)}}, + }...) + } + + return &protoSet +} + +// NewExprL4Proto returns a new expression to match a protocol. +func NewExprL4Proto(name string, cmpOp *expr.CmpOp) *[]expr.Any { + proto, _ := getProtocolCode(name) + return &[]expr.Any{ + &expr.Cmp{ + Op: *cmpOp, + Register: 1, + Data: []byte{byte(proto)}, + }, + } +} diff --git a/daemon/firewall/nftables/exprs/protocol_test.go b/daemon/firewall/nftables/exprs/protocol_test.go new file mode 100644 index 0000000..b2ed968 --- /dev/null +++ b/daemon/firewall/nftables/exprs/protocol_test.go @@ -0,0 +1,84 @@ +package exprs_test + +import ( + "fmt" + "reflect" + "testing" + + exprs "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/nftest" + "github.com/google/nftables/expr" + "golang.org/x/sys/unix" +) + +func TestExprProtocol(t *testing.T) { + nftest.SkipIfNotPrivileged(t) + + conn, newNS := nftest.OpenSystemConn(t) + defer nftest.CleanupSystemConn(t, newNS) + nftest.Fw.Conn = conn + + testProtos := []string{ + exprs.NFT_PROTO_TCP, + exprs.NFT_PROTO_UDP, + exprs.NFT_PROTO_UDPLITE, + exprs.NFT_PROTO_SCTP, + exprs.NFT_PROTO_DCCP, + exprs.NFT_PROTO_ICMP, + exprs.NFT_PROTO_ICMPv6, + } + protoValues := []byte{ + unix.IPPROTO_TCP, + unix.IPPROTO_UDP, + unix.IPPROTO_UDPLITE, + unix.IPPROTO_SCTP, + unix.IPPROTO_DCCP, + unix.IPPROTO_ICMP, + unix.IPPROTO_ICMPV6, + } + + for idx, proto := range testProtos { + t.Run(fmt.Sprint("test-protoExpr-", proto), func(t *testing.T) { + protoExpr, err := exprs.NewExprProtocol(proto) + if err != nil { + t.Errorf("%s - Error creating expr Log: %s", proto, protoExpr) + return + } + r, _ := nftest.AddTestRule(t, conn, protoExpr) + if r == nil { + t.Errorf("Error adding rule with proto %s expression", proto) + } + if len(r.Exprs) != 2 { + t.Errorf("%s - expected 2 Expressions, found %d", proto, len(r.Exprs)) + } + e := r.Exprs[0] + meta, ok := e.(*expr.Meta) + if !ok { + t.Errorf("%s - invalid proto expr: %T", proto, e) + } + //fmt.Printf("%s, %+v\n", reflect.TypeOf(e).String(), e) + if reflect.TypeOf(e).String() != "*expr.Meta" { + t.Errorf("%s - first expression should be *expr.Meta, instead of: %s", proto, reflect.TypeOf(e)) + } + if meta.Key != expr.MetaKeyL4PROTO { + t.Errorf("%s - invalid proto expr.Meta.Key: %d", proto, expr.MetaKeyL4PROTO) + } + + e = r.Exprs[1] + cmp, ok := e.(*expr.Cmp) + if !ok { + t.Errorf("%s - invalid proto cmp expr: %T", proto, e) + } + //fmt.Printf("%s, %+v\n", reflect.TypeOf(e).String(), e) + if reflect.TypeOf(e).String() != "*expr.Cmp" { + t.Errorf("%s - second expression should be *expr.Cmp, instead of: %s", proto, reflect.TypeOf(e)) + } + if cmp.Op != expr.CmpOpEq { + t.Errorf("%s - expr.Cmp should be CmpOpEq, instead of: %d", proto, cmp.Op) + } + if cmp.Data[0] != protoValues[idx] { + t.Errorf("%s - expr.Data differs: %d<->%d", proto, cmp.Data, protoValues[idx]) + } + }) + } +} diff --git a/daemon/firewall/nftables/exprs/quota.go b/daemon/firewall/nftables/exprs/quota.go new file mode 100644 index 0000000..5d705c7 --- /dev/null +++ b/daemon/firewall/nftables/exprs/quota.go @@ -0,0 +1,66 @@ +package exprs + +import ( + "fmt" + "strconv" + + "github.com/evilsocket/opensnitch/daemon/firewall/config" + "github.com/google/nftables/expr" +) + +// NewQuota returns a new quota expression. +// TODO: named quotas +func NewQuota(opts []*config.ExprValues) (*[]expr.Any, error) { + over := false + bytes := int64(0) + used := int64(0) + for _, opt := range opts { + switch opt.Key { + case NFT_QUOTA_OVER: + over = true + case NFT_QUOTA_UNIT_BYTES: + b, err := strconv.ParseInt(opt.Value, 10, 64) + if err != nil { + return nil, fmt.Errorf("invalid quota bytes: %s", opt.Value) + } + bytes = b + case NFT_QUOTA_USED: + // TODO: support for other size units + b, err := strconv.ParseInt(opt.Value, 10, 64) + if err != nil { + return nil, fmt.Errorf("invalid quota initial consumed bytes: %s", opt.Value) + } + used = b + case NFT_QUOTA_UNIT_KB: + b, err := strconv.ParseInt(opt.Value, 10, 64) + if err != nil { + return nil, fmt.Errorf("invalid quota bytes: %s", opt.Value) + } + bytes = b * 1024 + case NFT_QUOTA_UNIT_MB: + b, err := strconv.ParseInt(opt.Value, 10, 64) + if err != nil { + return nil, fmt.Errorf("invalid quota bytes: %s", opt.Value) + } + bytes = (b * 1024) * 1024 + case NFT_QUOTA_UNIT_GB: + b, err := strconv.ParseInt(opt.Value, 10, 64) + if err != nil { + return nil, fmt.Errorf("invalid quota bytes: %s", opt.Value) + } + bytes = ((b * 1024) * 1024) * 1024 + default: + return nil, fmt.Errorf("invalid quota key: %s", opt.Key) + } + } + if bytes == 0 { + return nil, fmt.Errorf("quota bytes cannot be 0") + } + return &[]expr.Any{ + &expr.Quota{ + Bytes: uint64(bytes), + Consumed: uint64(used), + Over: over, + }, + }, nil +} diff --git a/daemon/firewall/nftables/exprs/quota_test.go b/daemon/firewall/nftables/exprs/quota_test.go new file mode 100644 index 0000000..ebb7a91 --- /dev/null +++ b/daemon/firewall/nftables/exprs/quota_test.go @@ -0,0 +1,243 @@ +package exprs_test + +import ( + "testing" + + "github.com/evilsocket/opensnitch/daemon/firewall/config" + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/nftest" + "github.com/google/nftables/expr" +) + +func TestExprQuota(t *testing.T) { + nftest.SkipIfNotPrivileged(t) + + conn, newNS := nftest.OpenSystemConn(t) + defer nftest.CleanupSystemConn(t, newNS) + nftest.Fw.Conn = conn + + tests := []nftest.TestsT{ + { + "test-quota-over-bytes-12345", + "", // family + "", // parms + []*config.ExprValues{ + &config.ExprValues{ + Key: exprs.NFT_QUOTA_OVER, + Value: "", + }, + &config.ExprValues{ + Key: exprs.NFT_QUOTA_UNIT_BYTES, + Value: "12345", + }, + }, + 1, + []interface{}{ + &expr.Quota{ + Bytes: uint64(12345), + Consumed: 0, + Over: true, + }, + }, + false, + }, + { + "test-quota-over-kbytes-1", + "", // family + "", // parms + []*config.ExprValues{ + &config.ExprValues{ + Key: exprs.NFT_QUOTA_OVER, + Value: "", + }, + &config.ExprValues{ + Key: exprs.NFT_QUOTA_UNIT_KB, + Value: "1", + }, + }, + 1, + []interface{}{ + &expr.Quota{ + Bytes: uint64(1024), + Consumed: 0, + Over: true, + }, + }, + false, + }, + { + "test-quota-over-mbytes-1", + "", // family + "", // parms + []*config.ExprValues{ + &config.ExprValues{ + Key: exprs.NFT_QUOTA_OVER, + Value: "", + }, + &config.ExprValues{ + Key: exprs.NFT_QUOTA_UNIT_MB, + Value: "1", + }, + }, + 1, + []interface{}{ + &expr.Quota{ + Bytes: uint64(1024 * 1024), + Consumed: 0, + Over: true, + }, + }, + false, + }, + { + "test-quota-over-gbytes-1", + "", // family + "", // parms + []*config.ExprValues{ + &config.ExprValues{ + Key: exprs.NFT_QUOTA_OVER, + Value: "", + }, + &config.ExprValues{ + Key: exprs.NFT_QUOTA_UNIT_GB, + Value: "1", + }, + }, + 1, + []interface{}{ + &expr.Quota{ + Bytes: uint64(1024 * 1024 * 1024), + Consumed: 0, + Over: true, + }, + }, + false, + }, + { + "test-quota-until-gbytes-1", + "", // family + "", // parms + []*config.ExprValues{ + &config.ExprValues{ + Key: exprs.NFT_QUOTA_UNIT_GB, + Value: "1", + }, + }, + 1, + []interface{}{ + &expr.Quota{ + Bytes: uint64(1024 * 1024 * 1024), + Consumed: 0, + Over: false, + }, + }, + false, + }, + { + "test-quota-consumed-bytes-1024", + "", // family + "", // parms + []*config.ExprValues{ + &config.ExprValues{ + Key: exprs.NFT_QUOTA_UNIT_GB, + Value: "1", + }, + &config.ExprValues{ + Key: exprs.NFT_QUOTA_USED, + Value: "1024", + }, + }, + 1, + []interface{}{ + &expr.Quota{ + Bytes: uint64(1024 * 1024 * 1024), + Consumed: 1024, + Over: false, + }, + }, + false, + }, + { + "test-invalid-quota-key", + "", // family + "", // parms + []*config.ExprValues{ + &config.ExprValues{ + Key: "gbyte", + Value: "1", + }, + }, + 1, + []interface{}{}, + true, + }, + { + "test-invalid-quota-value", + "", // family + "", // parms + []*config.ExprValues{ + &config.ExprValues{ + Key: exprs.NFT_QUOTA_UNIT_GB, + Value: "1a", + }, + }, + 1, + []interface{}{}, + true, + }, + { + "test-invalid-quota-value", + "", // family + "", // parms + []*config.ExprValues{ + &config.ExprValues{ + Key: exprs.NFT_QUOTA_UNIT_GB, + Value: "", + }, + }, + 1, + []interface{}{}, + true, + }, + { + "test-invalid-quota-bytes-0", + "", // family + "", // parms + []*config.ExprValues{ + &config.ExprValues{ + Key: exprs.NFT_QUOTA_UNIT_GB, + Value: "0", + }, + }, + 1, + []interface{}{}, + true, + }, + } + + for _, test := range tests { + t.Run(test.Name, func(t *testing.T) { + quotaExpr, err := exprs.NewQuota(test.Values) + if err != nil && !test.ExpectedFail { + t.Errorf("Error creating expr Quota: %s", quotaExpr) + return + } else if err != nil && test.ExpectedFail { + return + } + + r, _ := nftest.AddTestRule(t, conn, quotaExpr) + if r == nil && !test.ExpectedFail { + t.Error("Error adding rule with Quota expression") + } + + if !nftest.AreExprsValid(t, &test, r) { + return + } + + if test.ExpectedFail { + t.Errorf("test should have failed") + } + }) + } + +} diff --git a/daemon/firewall/nftables/exprs/utils.go b/daemon/firewall/nftables/exprs/utils.go new file mode 100644 index 0000000..35081cf --- /dev/null +++ b/daemon/firewall/nftables/exprs/utils.go @@ -0,0 +1,181 @@ +package exprs + +import ( + "strconv" + + "github.com/google/gopacket/layers" + "golang.org/x/sys/unix" +) + +// GetICMPRejectCode returns the code by its name. +func GetICMPRejectCode(reason string) uint8 { + switch reason { + case ICMP_HOST_UNREACHABLE, ICMP_ADDR_UNREACHABLE: + return layers.ICMPv4CodeHost + case ICMP_PROT_UNREACHABLE: + return layers.ICMPv4CodeProtocol + case ICMP_PORT_UNREACHABLE: + return layers.ICMPv4CodePort + case ICMP_ADMIN_PROHIBITED: + return layers.ICMPv4CodeCommAdminProhibited + case ICMP_HOST_PROHIBITED: + return layers.ICMPv4CodeHostAdminProhibited + case ICMP_NET_PROHIBITED: + return layers.ICMPv4CodeNetAdminProhibited + } + + return layers.ICMPv4CodeNet +} + +// GetICMPxRejectCode returns the code by its name. +func GetICMPxRejectCode(reason string) uint8 { + // https://github.com/torvalds/linux/blob/master/net/netfilter/nft_reject.c#L96 + // https://github.com/google/gopacket/blob/3aa782ce48d4a525acaebab344cedabfb561f870/layers/icmp4.go#L37 + switch reason { + case ICMP_HOST_UNREACHABLE, ICMP_NET_UNREACHABLE: + return unix.NFT_REJECT_ICMP_UNREACH // results in -> net-unreachable??? + case ICMP_PROT_UNREACHABLE: + return unix.NFT_REJECT_ICMPX_HOST_UNREACH // results in -> prot-unreachable??? + case ICMP_PORT_UNREACHABLE: + return unix.NFT_REJECT_ICMPX_PORT_UNREACH // results in -> host-unreachable??? + case ICMP_NO_ROUTE: + return unix.NFT_REJECT_ICMPX_NO_ROUTE // results in -> net-unreachable + } + + return unix.NFT_REJECT_ICMP_UNREACH // results in -> net-unreachable??? +} + +// GetICMPType returns an ICMP type code +func GetICMPType(icmpType string) uint8 { + switch icmpType { + case ICMP_ECHO_REPLY: + return layers.ICMPv4TypeEchoReply + case ICMP_ECHO_REQUEST: + return layers.ICMPv4TypeEchoRequest + case ICMP_SOURCE_QUENCH: + return layers.ICMPv4TypeSourceQuench + case ICMP_DEST_UNREACHABLE: + return layers.ICMPv4TypeDestinationUnreachable + case ICMP_ROUTER_ADVERTISEMENT: + return layers.ICMPv4TypeRouterAdvertisement + case ICMP_ROUTER_SOLICITATION: + return layers.ICMPv4TypeRouterSolicitation + case ICMP_REDIRECT: + return layers.ICMPv4TypeRedirect + case ICMP_TIME_EXCEEDED: + return layers.ICMPv4TypeTimeExceeded + case ICMP_INFO_REQUEST: + return layers.ICMPv4TypeInfoRequest + case ICMP_INFO_REPLY: + return layers.ICMPv4TypeInfoReply + case ICMP_PARAMETER_PROBLEM: + return layers.ICMPv4TypeParameterProblem + case ICMP_TIMESTAMP_REQUEST: + return layers.ICMPv4TypeTimestampRequest + case ICMP_TIMESTAMP_REPLY: + return layers.ICMPv4TypeTimestampReply + case ICMP_ADDRESS_MASK_REQUEST: + return layers.ICMPv4TypeAddressMaskRequest + case ICMP_ADDRESS_MASK_REPLY: + return layers.ICMPv4TypeAddressMaskReply + } + return 0 +} + +// GetICMPv6Type returns an ICMPv6 type code +func GetICMPv6Type(icmpType string) uint8 { + switch icmpType { + case ICMP_DEST_UNREACHABLE: + return layers.ICMPv6TypeDestinationUnreachable + case ICMP_PACKET_TOO_BIG: + return layers.ICMPv6TypePacketTooBig + case ICMP_TIME_EXCEEDED: + return layers.ICMPv6TypeTimeExceeded + case ICMP_PARAMETER_PROBLEM: + return layers.ICMPv6TypeParameterProblem + case ICMP_ECHO_REQUEST: + return layers.ICMPv6TypeEchoRequest + case ICMP_ECHO_REPLY: + return layers.ICMPv6TypeEchoReply + case ICMP_ROUTER_SOLICITATION: + return layers.ICMPv6TypeRouterSolicitation + case ICMP_ROUTER_ADVERTISEMENT: + return layers.ICMPv6TypeRouterAdvertisement + case ICMP_NEIGHBOUR_SOLICITATION: + return layers.ICMPv6TypeNeighborSolicitation + case ICMP_NEIGHBOUR_ADVERTISEMENT: + return layers.ICMPv6TypeNeighborAdvertisement + case ICMP_REDIRECT: + return layers.ICMPv6TypeRedirect + } + return 0 +} + +// GetICMPv6RejectCode returns the code by its name. +func GetICMPv6RejectCode(reason string) uint8 { + switch reason { + case ICMP_HOST_UNREACHABLE, ICMP_NET_UNREACHABLE, ICMP_NO_ROUTE: + return layers.ICMPv6CodeNoRouteToDst + case ICMP_ADDR_UNREACHABLE: + return layers.ICMPv6CodeAddressUnreachable + case ICMP_PORT_UNREACHABLE: + return layers.ICMPv6CodePortUnreachable + case ICMP_REJECT_POLICY_FAIL: + return layers.ICMPv6CodeSrcAddressFailedPolicy + case ICMP_REJECT_ROUTE: + return layers.ICMPv6CodeRejectRouteToDst + } + + return layers.ICMPv6CodeNoRouteToDst +} + +// getProtocolCode will try to return the code of the given protocol. +// If the protocol is not in our list, we'll use the value as decimal. +// So for example IPPROTO_ENCAP (0x62) must be specified as 98. +// https://pkg.go.dev/golang.org/x/sys/unix#pkg-constants +func getProtocolCode(value string) (byte, error) { + switch value { + case NFT_PROTO_TCP: + return unix.IPPROTO_TCP, nil + case NFT_PROTO_UDP: + return unix.IPPROTO_UDP, nil + case NFT_PROTO_UDPLITE: + return unix.IPPROTO_UDPLITE, nil + case NFT_PROTO_SCTP: + return unix.IPPROTO_SCTP, nil + case NFT_PROTO_DCCP: + return unix.IPPROTO_DCCP, nil + case NFT_PROTO_ICMP: + return unix.IPPROTO_ICMP, nil + case NFT_PROTO_ICMPv6: + return unix.IPPROTO_ICMPV6, nil + case NFT_PROTO_AH: + return unix.IPPROTO_AH, nil + case NFT_PROTO_ETHERNET: + return unix.IPPROTO_ETHERNET, nil + case NFT_PROTO_GRE: + return unix.IPPROTO_GRE, nil + case NFT_PROTO_IP: + return unix.IPPROTO_IP, nil + case NFT_PROTO_IPIP: + return unix.IPPROTO_IPIP, nil + case NFT_PROTO_L2TP: + return unix.IPPROTO_L2TP, nil + case NFT_PROTO_COMP: + return unix.IPPROTO_COMP, nil + case NFT_PROTO_IGMP: + return unix.IPPROTO_IGMP, nil + case NFT_PROTO_ESP: + return unix.IPPROTO_ESP, nil + case NFT_PROTO_RAW: + return unix.IPPROTO_RAW, nil + case NFT_PROTO_ENCAP: + return unix.IPPROTO_ENCAP, nil + } + + prot, err := strconv.Atoi(value) + if err != nil { + return 0, err + } + return byte(prot), nil +} diff --git a/daemon/firewall/nftables/exprs/verdict.go b/daemon/firewall/nftables/exprs/verdict.go new file mode 100644 index 0000000..54a0232 --- /dev/null +++ b/daemon/firewall/nftables/exprs/verdict.go @@ -0,0 +1,202 @@ +package exprs + +import ( + "strconv" + "strings" + + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/google/nftables/expr" + "golang.org/x/sys/unix" +) + +// NewExprVerdict constructs a new verdict to apply on connections. +func NewExprVerdict(verdict, parms string) *[]expr.Any { + switch strings.ToLower(verdict) { + case VERDICT_ACCEPT: + return NewExprAccept() + + case VERDICT_DROP: + return &[]expr.Any{&expr.Verdict{ + Kind: expr.VerdictDrop, + }} + + // FIXME: this verdict is not added to nftables + case VERDICT_STOP: + return &[]expr.Any{&expr.Verdict{ + Kind: expr.VerdictStop, + }} + + case VERDICT_REJECT: + reject := NewExprReject(parms) + return &[]expr.Any{reject} + + case VERDICT_RETURN: + return &[]expr.Any{&expr.Verdict{ + Kind: expr.VerdictReturn, + }} + + case VERDICT_JUMP: + return &[]expr.Any{ + &expr.Verdict{ + Kind: expr.VerdictKind(unix.NFT_JUMP), + Chain: parms, + }, + } + + case VERDICT_QUEUE: + queueNum := 0 + var err error + p := strings.Split(parms, " ") + if len(p) == 0 { + log.Warning("invalid Queue expr parameters") + return nil + } + // TODO: allow to configure this flag + if p[0] == NFT_QUEUE_NUM { + queueNum, err = strconv.Atoi(p[len(p)-1]) + if err != nil { + log.Warning("invalid Queue num: %s", err) + return nil + } + } + + return &[]expr.Any{ + &expr.Queue{ + Num: uint16(queueNum), + Flag: expr.QueueFlagBypass, + }} + + case VERDICT_SNAT: + snat := NewExprSNAT() + snat.Random, snat.FullyRandom, snat.Persistent = NewExprNATFlags(parms) + snatExpr := &[]expr.Any{snat} + + regAddr, regProto, natParms, err := NewExprNAT(parms, VERDICT_SNAT) + if err != nil { + log.Warning("error adding snat verdict: %s", err) + return nil + } + if regAddr { + snat.RegAddrMin = 1 + } + if regProto { + snat.RegProtoMin = 2 + } + *snatExpr = append(*natParms, *snatExpr...) + return snatExpr + + case VERDICT_DNAT: + dnat := NewExprDNAT() + dnat.Random, dnat.FullyRandom, dnat.Persistent = NewExprNATFlags(parms) + dnatExpr := &[]expr.Any{dnat} + + regAddr, regProto, natParms, err := NewExprNAT(parms, VERDICT_DNAT) + if err != nil { + log.Warning("error adding dnat verdict: %s", err) + return nil + } + + if regAddr { + dnat.RegAddrMin = 1 + } + if regProto { + dnat.RegProtoMin = 2 + } + *dnatExpr = append(*natParms, *dnatExpr...) + + return dnatExpr + + case VERDICT_MASQUERADE: + m := &expr.Masq{} + m.Random, m.FullyRandom, m.Persistent = NewExprNATFlags(parms) + masqExpr := &[]expr.Any{m} + + if parms == "" { + return masqExpr + } + // if any of the flag is set to true, toPorts must be false + toPorts := !(m.Random == true || m.FullyRandom == true || m.Persistent == true) + masqExpr = NewExprMasquerade(toPorts, m.Random, m.FullyRandom, m.Persistent) + _, _, natParms, err := NewExprNAT(parms, VERDICT_MASQUERADE) + if err != nil { + log.Warning("error adding masquerade verdict: %s", err) + } + *masqExpr = append(*natParms, *masqExpr...) + + return masqExpr + + case VERDICT_REDIRECT: + _, _, rewriteParms, err := NewExprNAT(parms, VERDICT_REDIRECT) + if err != nil { + log.Warning("error adding redirect verdict: %s", err) + return nil + } + redirExpr := NewExprRedirect() + *redirExpr = append(*rewriteParms, *redirExpr...) + return redirExpr + + case VERDICT_TPROXY: + _, _, rewriteParms, err := NewExprNAT(parms, VERDICT_TPROXY) + if err != nil { + log.Warning("error adding tproxy verdict: %s", err) + return nil + } + tproxyExpr := &[]expr.Any{} + *tproxyExpr = append(*tproxyExpr, *rewriteParms...) + tVerdict := NewExprTproxy() + *tproxyExpr = append(*tproxyExpr, *tVerdict...) + *tproxyExpr = append(*tproxyExpr, *NewExprAccept()...) + return tproxyExpr + + } + + // target can be empty, "ct set mark" or "log" for example + return &[]expr.Any{} +} + +// NewExprAccept creates the accept verdict. +func NewExprAccept() *[]expr.Any { + return &[]expr.Any{&expr.Verdict{ + Kind: expr.VerdictAccept, + }} +} + +// NewExprReject creates new Reject expression +// icmpx, to reject the IPv4 and IPv6 traffic, icmp for ipv4, icmpv6 for ... +// Ex.: "Target": "reject", "TargetParameters": "with tcp reset" +// https://wiki.nftables.org/wiki-nftables/index.php/Rejecting_traffic +func NewExprReject(parms string) *expr.Reject { + reject := &expr.Reject{} + reject.Code = unix.NFT_REJECT_ICMP_UNREACH + reject.Type = unix.NFT_REJECT_ICMP_UNREACH + + parmList := strings.Split(parms, " ") + length := len(parmList) + if length <= 1 { + return reject + } + what := parmList[1] + how := parmList[length-1] + + switch what { + case NFT_PROTO_TCP: + reject.Type = unix.NFT_REJECT_TCP_RST + reject.Code = unix.NFT_REJECT_TCP_RST + case NFT_PROTO_ICMP: + reject.Type = unix.NFT_REJECT_ICMP_UNREACH + reject.Code = GetICMPRejectCode(how) + return reject + case NFT_PROTO_ICMPX: + // icmp and icmpv6 + reject.Type = unix.NFT_REJECT_ICMPX_UNREACH + reject.Code = GetICMPxRejectCode(how) + return reject + case NFT_PROTO_ICMPv6: + reject.Type = 1 + reject.Code = GetICMPv6RejectCode(how) + + default: + } + + return reject +} diff --git a/daemon/firewall/nftables/exprs/verdict_test.go b/daemon/firewall/nftables/exprs/verdict_test.go new file mode 100644 index 0000000..de34b3b --- /dev/null +++ b/daemon/firewall/nftables/exprs/verdict_test.go @@ -0,0 +1,318 @@ +package exprs_test + +import ( + "fmt" + "reflect" + "testing" + + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/nftest" + "github.com/google/nftables" + "github.com/google/nftables/expr" + "golang.org/x/sys/unix" +) + +type verdictTestsT struct { + name string + verdict string + parms string + expectedExpr string + expectedKind expr.VerdictKind +} + +func TestExprVerdict(t *testing.T) { + nftest.SkipIfNotPrivileged(t) + + conn, newNS := nftest.OpenSystemConn(t) + defer nftest.CleanupSystemConn(t, newNS) + nftest.Fw.Conn = conn + + // we must create a custom chain before using JUMP verdict. + tbl, _ := nftest.Fw.AddTable("yyy", exprs.NFT_FAMILY_INET) + nftest.Fw.Conn.AddChain(&nftables.Chain{ + Name: "custom-chain", + Table: tbl, + }) + nftest.Fw.Commit() + + verdictTests := []verdictTestsT{ + {"test-accept", exprs.VERDICT_ACCEPT, "", "*expr.Verdict", expr.VerdictAccept}, + {"test-AcCept", "AcCePt", "", "*expr.Verdict", expr.VerdictAccept}, + {"test-ACCEPT", "ACCEPT", "", "*expr.Verdict", expr.VerdictAccept}, + {"test-drop", exprs.VERDICT_DROP, "", "*expr.Verdict", expr.VerdictDrop}, + //{"test-stop", exprs.VERDICT_STOP, "", "*expr.Verdict", expr.VerdictStop}, + {"test-return", exprs.VERDICT_RETURN, "", "*expr.Verdict", expr.VerdictReturn}, + {"test-jump", exprs.VERDICT_JUMP, "custom-chain", "*expr.Verdict", expr.VerdictJump}, + // empty verdict must be valid at this level. + // it can be used with "log" or "ct set mark" + {"test-empty-verdict", "", "", "*expr.Verdict", expr.VerdictAccept}, + } + + for _, test := range verdictTests { + t.Run(test.name, func(t *testing.T) { + verdExpr := exprs.NewExprVerdict(test.verdict, test.parms) + r, _ := nftest.AddTestRule(t, conn, verdExpr) + if r == nil { + t.Errorf("Error adding rule with verdict expression %s", test.verdict) + return + } + if test.name == "test-empty-verdict" { + return + } + e := r.Exprs[0] + if reflect.TypeOf(e).String() != test.expectedExpr { + t.Errorf("first expression should be *expr.Verdict, instead of: %s", reflect.TypeOf(e)) + return + } + verd, ok := e.(*expr.Verdict) + if !ok { + t.Errorf("invalid verdict: %T", e) + return + } + if verd.Kind != test.expectedKind { + t.Errorf("invalid verdict kind: %+v, expected: %+v", verd.Kind, test.expectedKind) + return + } + }) + } +} + +func TestExprVerdictReject(t *testing.T) { + nftest.SkipIfNotPrivileged(t) + + conn, newNS := nftest.OpenSystemConn(t) + defer nftest.CleanupSystemConn(t, newNS) + nftest.Fw.Conn = conn + + type rejectTests struct { + name string + parms string + what string + family string + parmType byte + parmCode byte + } + tests := []rejectTests{ + { + "test-reject-tcp-RST", + "with tcp reset", + exprs.NFT_PROTO_TCP, + exprs.NFT_FAMILY_INET, + unix.NFT_REJECT_TCP_RST, + unix.NFT_REJECT_TCP_RST, + }, + + { + "test-reject-icmp-host-unreachable", + fmt.Sprint("with icmp ", exprs.ICMP_HOST_UNREACHABLE), + exprs.NFT_FAMILY_IP, + exprs.NFT_PROTO_ICMP, + unix.NFT_REJECT_ICMP_UNREACH, + exprs.GetICMPRejectCode(exprs.ICMP_HOST_UNREACHABLE), + }, + { + "test-reject-icmp-addr-unreachable", + fmt.Sprint("with icmp ", exprs.ICMP_ADDR_UNREACHABLE), + exprs.NFT_FAMILY_IP, + exprs.NFT_PROTO_ICMP, + unix.NFT_REJECT_ICMP_UNREACH, + exprs.GetICMPRejectCode(exprs.ICMP_ADDR_UNREACHABLE), + }, + { + "test-reject-icmp-prot-unreachable", + fmt.Sprint("with icmp ", exprs.ICMP_PROT_UNREACHABLE), + exprs.NFT_FAMILY_IP, + exprs.NFT_PROTO_ICMP, + unix.NFT_REJECT_ICMP_UNREACH, + exprs.GetICMPRejectCode(exprs.ICMP_PROT_UNREACHABLE), + }, + { + "test-reject-icmp-port-unreachable", + fmt.Sprint("with icmp ", exprs.ICMP_PORT_UNREACHABLE), + exprs.NFT_FAMILY_IP, + exprs.NFT_PROTO_ICMP, + unix.NFT_REJECT_ICMP_UNREACH, + exprs.GetICMPRejectCode(exprs.ICMP_PORT_UNREACHABLE), + }, + { + "test-reject-icmp-admin-prohibited", + fmt.Sprint("with icmp ", exprs.ICMP_ADMIN_PROHIBITED), + exprs.NFT_FAMILY_IP, + exprs.NFT_PROTO_ICMP, + unix.NFT_REJECT_ICMP_UNREACH, + exprs.GetICMPRejectCode(exprs.ICMP_ADMIN_PROHIBITED), + }, + { + "test-reject-icmp-host-prohibited", + fmt.Sprint("with icmp ", exprs.ICMP_HOST_PROHIBITED), + exprs.NFT_FAMILY_IP, + exprs.NFT_PROTO_ICMP, + unix.NFT_REJECT_ICMP_UNREACH, + exprs.GetICMPRejectCode(exprs.ICMP_HOST_PROHIBITED), + }, + { + "test-reject-icmp-net-prohibited", + fmt.Sprint("with icmp ", exprs.ICMP_NET_PROHIBITED), + exprs.NFT_FAMILY_IP, + exprs.NFT_PROTO_ICMP, + unix.NFT_REJECT_ICMP_UNREACH, + exprs.GetICMPRejectCode(exprs.ICMP_NET_PROHIBITED), + }, + + // icmpx + { + "test-reject-icmpx-net-unreachable", + fmt.Sprint("with icmpx ", exprs.ICMP_NET_UNREACHABLE), + exprs.NFT_FAMILY_INET, + exprs.NFT_PROTO_ICMPX, + unix.NFT_REJECT_ICMPX_UNREACH, + exprs.GetICMPxRejectCode(exprs.ICMP_NET_UNREACHABLE), + }, + { + "test-reject-icmpx-host-unreachable", + fmt.Sprint("with icmpx ", exprs.ICMP_HOST_UNREACHABLE), + exprs.NFT_FAMILY_INET, + exprs.NFT_PROTO_ICMPX, + unix.NFT_REJECT_ICMPX_UNREACH, + exprs.GetICMPxRejectCode(exprs.ICMP_HOST_UNREACHABLE), + }, + { + "test-reject-icmpx-prot-unreachable", + fmt.Sprint("with icmpx ", exprs.ICMP_PROT_UNREACHABLE), + exprs.NFT_FAMILY_INET, + exprs.NFT_PROTO_ICMPX, + unix.NFT_REJECT_ICMPX_UNREACH, + exprs.GetICMPxRejectCode(exprs.ICMP_PROT_UNREACHABLE), + }, + { + "test-reject-icmpx-port-unreachable", + fmt.Sprint("with icmpx ", exprs.ICMP_PORT_UNREACHABLE), + exprs.NFT_FAMILY_INET, + exprs.NFT_PROTO_ICMPX, + unix.NFT_REJECT_ICMPX_UNREACH, + exprs.GetICMPxRejectCode(exprs.ICMP_PORT_UNREACHABLE), + }, + { + "test-reject-icmpx-no-route", + fmt.Sprint("with icmpx ", exprs.ICMP_NO_ROUTE), + exprs.NFT_FAMILY_INET, + exprs.NFT_PROTO_ICMPX, + unix.NFT_REJECT_ICMPX_UNREACH, + exprs.GetICMPxRejectCode(exprs.ICMP_NO_ROUTE), + }, + + // icmpv6 + { + "test-reject-icmpv6-net-unreachable", + fmt.Sprint("with icmpv6 ", exprs.ICMP_NET_UNREACHABLE), + exprs.NFT_FAMILY_IP6, + exprs.NFT_PROTO_ICMPv6, + 1, + exprs.GetICMPv6RejectCode(exprs.ICMP_NET_UNREACHABLE), + }, + { + "test-reject-icmpv6-addr-unreachable", + fmt.Sprint("with icmpv6 ", exprs.ICMP_ADDR_UNREACHABLE), + exprs.NFT_FAMILY_IP6, + exprs.NFT_PROTO_ICMPv6, + 1, + exprs.GetICMPv6RejectCode(exprs.ICMP_ADDR_UNREACHABLE), + }, + { + "test-reject-icmpv6-host-unreachable", + fmt.Sprint("with icmpv6 ", exprs.ICMP_HOST_UNREACHABLE), + exprs.NFT_FAMILY_IP6, + exprs.NFT_PROTO_ICMPv6, + 1, + exprs.GetICMPv6RejectCode(exprs.ICMP_HOST_UNREACHABLE), + }, + { + "test-reject-icmpv6-port-unreachable", + fmt.Sprint("with icmpv6 ", exprs.ICMP_PORT_UNREACHABLE), + exprs.NFT_FAMILY_IP6, + exprs.NFT_PROTO_ICMPv6, + 1, + exprs.GetICMPv6RejectCode(exprs.ICMP_PORT_UNREACHABLE), + }, + { + "test-reject-icmpv6-no-route", + fmt.Sprint("with icmpv6 ", exprs.ICMP_NO_ROUTE), + exprs.NFT_FAMILY_IP6, + exprs.NFT_PROTO_ICMPv6, + 1, + exprs.GetICMPv6RejectCode(exprs.ICMP_NO_ROUTE), + }, + { + "test-reject-icmpv6-reject-policy-fail", + fmt.Sprint("with icmpv6 ", exprs.ICMP_REJECT_POLICY_FAIL), + exprs.NFT_FAMILY_IP6, + exprs.NFT_PROTO_ICMPv6, + 1, + exprs.GetICMPv6RejectCode(exprs.ICMP_REJECT_POLICY_FAIL), + }, + { + "test-reject-icmpv6-reject-route", + fmt.Sprint("with icmpv6 ", exprs.ICMP_REJECT_ROUTE), + exprs.NFT_FAMILY_IP6, + exprs.NFT_PROTO_ICMPv6, + 1, + exprs.GetICMPv6RejectCode(exprs.ICMP_REJECT_ROUTE), + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + verdExpr := exprs.NewExprVerdict(exprs.VERDICT_REJECT, test.parms) + r, _ := nftest.AddTestRule(t, conn, verdExpr) + if r == nil { + t.Errorf("Error adding rule with reject verdict %s", "") + return + } + e := r.Exprs[0] + if reflect.TypeOf(e).String() != "*expr.Reject" { + t.Errorf("first expression should be *expr.Verdict, instead of: %s", reflect.TypeOf(e)) + return + } + verd, ok := e.(*expr.Reject) + if !ok { + t.Errorf("invalid verdict: %T", e) + return + } + //fmt.Printf("reject verd: %+v\n", verd) + + if verd.Code != uint8(test.parmCode) { + t.Errorf("invalid reject verdict code: %d, expected: %d", verd.Code, test.parmCode) + } + + }) + } +} + +func TestExprVerdictQueue(t *testing.T) { + nftest.SkipIfNotPrivileged(t) + + conn, newNS := nftest.OpenSystemConn(t) + defer nftest.CleanupSystemConn(t, newNS) + nftest.Fw.Conn = conn + + verdExpr := exprs.NewExprVerdict(exprs.VERDICT_QUEUE, "num 1") + r, _ := nftest.AddTestRule(t, conn, verdExpr) + if r == nil { + t.Errorf("Error adding rule with Queue verdict") + return + } + e := r.Exprs[0] + if reflect.TypeOf(e).String() != "*expr.Queue" { + t.Errorf("first expression should be *expr.Queue, instead of: %s", reflect.TypeOf(e)) + return + } + verd, ok := e.(*expr.Queue) + if !ok { + t.Errorf("invalid verdict: %T", e) + return + } + if verd.Num != 1 { + t.Errorf("invalid queue verdict Num: %d", verd.Num) + } + +} diff --git a/daemon/firewall/nftables/monitor.go b/daemon/firewall/nftables/monitor.go new file mode 100644 index 0000000..88f3d62 --- /dev/null +++ b/daemon/firewall/nftables/monitor.go @@ -0,0 +1,72 @@ +package nftables + +import ( + "time" + + "github.com/evilsocket/opensnitch/daemon/firewall/common" + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" + "github.com/evilsocket/opensnitch/daemon/log" +) + +// AreRulesLoaded checks if the firewall rules for intercept traffic are loaded. +func (n *Nft) AreRulesLoaded() bool { + n.Lock() + defer n.Unlock() + + nRules := 0 + chains, err := n.Conn.ListChains() + if err != nil { + log.Warning("[nftables] error listing nftables chains: %s", err) + return false + } + + for _, c := range chains { + rules, err := n.Conn.GetRule(c.Table, c) + if err != nil { + log.Warning("[nftables] Error listing rules: %s", err) + continue + } + for rdx, r := range rules { + if string(r.UserData) == InterceptionRuleKey { + if c.Table.Name == exprs.NFT_CHAIN_FILTER && c.Name == exprs.NFT_HOOK_INPUT && rdx != 0 { + log.Warning("nftables DNS rule not in 1st position (%d)", rdx) + return false + } + nRules++ + if c.Table.Name == exprs.NFT_CHAIN_MANGLE && rdx < len(rules)-2 { + log.Warning("nfables queue rule is not the latest of the list (%d/%d), reloading", rdx, len(rules)) + return false + } + } + } + } + // we expect to have exactly 3 rules (2 queue and 1 dns). If there're less or more, then we + // need to reload them. + if nRules != 3 { + log.Warning("nfables filter rules not loaded: %d", nRules) + return false + } + + return true +} + +// ReloadConfCallback gets called after the configuration changes. +func (n *Nft) ReloadConfCallback() { + log.Important("reloadConfCallback changed, reloading") + n.DeleteSystemRules(!common.ForcedDelRules, !common.RestoreChains, log.GetLogLevel() == log.DEBUG) + n.AddSystemRules(common.ReloadRules, !common.BackupChains) +} + +// ReloadRulesCallback gets called when the interception rules are not present. +func (n *Nft) ReloadRulesCallback() { + log.Important("nftables firewall rules changed, reloading") + n.DisableInterception(log.GetLogLevel() == log.DEBUG) + time.Sleep(time.Millisecond * 500) + n.EnableInterception() +} + +// PreloadConfCallback gets called before the fw configuration is loaded +func (n *Nft) PreloadConfCallback() { + log.Info("nftables config changed, reloading") + n.DeleteSystemRules(!common.ForcedDelRules, common.RestoreChains, log.GetLogLevel() == log.DEBUG) +} diff --git a/daemon/firewall/nftables/monitor_test.go b/daemon/firewall/nftables/monitor_test.go new file mode 100644 index 0000000..775c4bb --- /dev/null +++ b/daemon/firewall/nftables/monitor_test.go @@ -0,0 +1,95 @@ +package nftables_test + +import ( + "testing" + "time" + + "github.com/evilsocket/opensnitch/daemon/firewall/common" + nftb "github.com/evilsocket/opensnitch/daemon/firewall/nftables" + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/nftest" + "github.com/google/nftables" +) + +// mimic EnableInterception() but without NewRulesChecker() +func addInterceptionRules(nft *nftb.Nft, t *testing.T) { + if err := nft.AddInterceptionTables(); err != nil { + t.Errorf("Error while adding interception tables: %s", err) + return + } + if err := nft.AddInterceptionChains(); err != nil { + t.Errorf("Error while adding interception chains: %s", err) + return + } + + if err, _ := nft.QueueDNSResponses(common.EnableRule, common.EnableRule); err != nil { + t.Errorf("Error while running DNS nftables rule: %s", err) + } + if err, _ := nft.QueueConnections(common.EnableRule, common.EnableRule); err != nil { + t.Errorf("Error while running conntrack nftables rule: %s", err) + } +} + +func _testMonitorReload(t *testing.T, conn *nftables.Conn, nft *nftb.Nft) { + tblfilter := nft.GetTable(exprs.NFT_CHAIN_FILTER, exprs.NFT_FAMILY_INET) + if tblfilter == nil || tblfilter.Name != exprs.NFT_CHAIN_FILTER { + t.Error("table filter-inet not in the list") + } + chnFilterInput := nftest.Fw.GetChain(exprs.NFT_HOOK_INPUT, tblfilter, exprs.NFT_FAMILY_INET) + if chnFilterInput == nil { + t.Error("chain input-filter-inet not in the list") + } + rules, _ := conn.GetRules(tblfilter, chnFilterInput) + if len(rules) == 0 { + t.Error("DNS interception rule not added") + } + conn.FlushChain(chnFilterInput) + nftest.Fw.Commit() + + // the rules checker checks the rules every 10s + reloaded := false + for i := 0; i < 15; i++ { + if r, _ := getRule(t, conn, tblfilter.Name, exprs.NFT_HOOK_INPUT, nftb.InterceptionRuleKey, 0); r != nil { + reloaded = true + break + } + time.Sleep(time.Second) + } + if !reloaded { + t.Error("rules under input-filter-inet not reloaded after 10s") + } +} + +func TestAreRulesLoaded(t *testing.T) { + nftest.SkipIfNotPrivileged(t) + + conn, newNS := nftest.OpenSystemConn(t) + defer nftest.CleanupSystemConn(t, newNS) + nftest.Fw.Conn = conn + + addInterceptionRules(nftest.Fw, t) + if !nftest.Fw.AreRulesLoaded() { + t.Error("interception rules not loaded, and they should") + } + + nftest.Fw.DelInterceptionRules() + if nftest.Fw.AreRulesLoaded() { + t.Error("interception rules are loaded, and the shouldn't") + } +} + +func TestMonitorReload(t *testing.T) { + nftest.SkipIfNotPrivileged(t) + + conn, newNS := nftest.OpenSystemConn(t) + defer nftest.CleanupSystemConn(t, newNS) + nftest.Fw.Conn = conn + + nftest.Fw.EnableInterception() + + // test that rules are reloaded after being deleted, but also + // that the monitor is not stopped after the first reload. + _testMonitorReload(t, conn, nftest.Fw) + _testMonitorReload(t, conn, nftest.Fw) + _testMonitorReload(t, conn, nftest.Fw) +} diff --git a/daemon/firewall/nftables/nftables.go b/daemon/firewall/nftables/nftables.go new file mode 100644 index 0000000..c0b314b --- /dev/null +++ b/daemon/firewall/nftables/nftables.go @@ -0,0 +1,195 @@ +package nftables + +import ( + "bytes" + "encoding/json" + "strings" + "sync" + + "github.com/evilsocket/opensnitch/daemon/firewall/common" + "github.com/evilsocket/opensnitch/daemon/firewall/config" + "github.com/evilsocket/opensnitch/daemon/firewall/iptables" + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/evilsocket/opensnitch/daemon/ui/protocol" + "github.com/golang/protobuf/jsonpb" + "github.com/google/nftables" +) + +// Action is the modifier we apply to a rule. +type Action string + +// Actions we apply to the firewall. +const ( + fwKey = "opensnitch-key" + InterceptionRuleKey = fwKey + "-interception" + SystemRuleKey = fwKey + "-system" + Name = "nftables" +) + +var ( + filterTable = &nftables.Table{ + Family: nftables.TableFamilyINet, + Name: exprs.NFT_CHAIN_FILTER, + } + + mangleTable = &nftables.Table{ + Family: nftables.TableFamilyINet, + Name: exprs.NFT_CHAIN_FILTER, + } +) + +// Nft holds the fields of our nftables firewall +type Nft struct { + Conn *nftables.Conn + chains iptables.SystemChains + common.Common + config.Config + + sync.Mutex +} + +// NewNft creates a new nftables object +func NewNft() *nftables.Conn { + return &nftables.Conn{} +} + +// Fw initializes a new nftables object +func Fw() (*Nft, error) { + n := &Nft{ + chains: iptables.SystemChains{ + Rules: make(map[string]*iptables.SystemRule), + }, + } + return n, nil +} + +// Name returns the name of the firewall +func (n *Nft) Name() string { + return Name +} + +// Init inserts the firewall rules and starts monitoring for firewall +// changes. +func (n *Nft) Init(qNum *int) { + if n.IsRunning() { + return + } + n.ErrChan = make(chan string, 100) + InitMapsStore() + n.SetQueueNum(qNum) + n.Conn = NewNft() + + // In order to clean up any existing firewall rule before start, + // we need to load the fw configuration first to know what rules + // were configured. + n.NewSystemFwConfig(n.PreloadConfCallback, n.ReloadConfCallback) + n.LoadDiskConfiguration(!common.ReloadConf) + + // start from a clean state + // The daemon may have exited unexpectedly, leaving residual fw rules, so we + // need to clean them up to avoid duplicated rules. + n.DelInterceptionRules() + n.AddSystemRules(!common.ReloadRules, common.BackupChains) + n.EnableInterception() + + n.Running = true +} + +// Stop deletes the firewall rules, allowing network traffic. +func (n *Nft) Stop() { + if n.IsRunning() == false { + return + } + n.StopConfigWatcher() + n.StopCheckingRules() + n.CleanRules(log.GetLogLevel() == log.DEBUG) + + n.Lock() + n.Running = false + n.Unlock() +} + +// EnableInterception adds firewall rules to intercept connections +func (n *Nft) EnableInterception() { + if err := n.AddInterceptionTables(); err != nil { + log.Error("Error while adding interception tables: %s", err) + return + } + if err := n.AddInterceptionChains(); err != nil { + log.Error("Error while adding interception chains: %s", err) + return + } + + if err, _ := n.QueueDNSResponses(common.EnableRule, common.EnableRule); err != nil { + log.Error("Error while running DNS nftables rule: %s", err) + } + if err, _ := n.QueueConnections(common.EnableRule, common.EnableRule); err != nil { + log.Error("Error while running conntrack nftables rule: %s", err) + } + // start monitoring firewall rules to intercept network traffic. + n.NewRulesChecker(n.AreRulesLoaded, n.ReloadRulesCallback) +} + +// DisableInterception removes firewall rules to intercept outbound connections. +func (n *Nft) DisableInterception(logErrors bool) { + n.StopCheckingRules() + n.DelInterceptionRules() +} + +// CleanRules deletes the rules we added. +func (n *Nft) CleanRules(logErrors bool) { + n.DisableInterception(logErrors) + n.DeleteSystemRules(common.ForcedDelRules, common.RestoreChains, logErrors) +} + +// Commit applies the queued changes, creating new objects (tables, chains, etc). +// You add rules, chains or tables, and after calling to Flush() they're added to the system. +// NOTE: it's very important not to call Flush() without queued tasks. +func (n *Nft) Commit() bool { + if err := n.Conn.Flush(); err != nil { + log.Warning("%s error applying changes: %s", logTag, err) + return false + } + return true +} + +// Serialize converts the configuration from json to protobuf +func (n *Nft) Serialize() (*protocol.SysFirewall, error) { + sysfw := &protocol.SysFirewall{} + jun := jsonpb.Unmarshaler{ + AllowUnknownFields: true, + } + rawConfig, err := json.Marshal(&n.SysConfig) + if err != nil { + log.Error("nftables.Serialize() struct to string error: %s", err) + return nil, err + } + // string to proto + if err := jun.Unmarshal(strings.NewReader(string(rawConfig)), sysfw); err != nil { + log.Error("nftables.Serialize() string to protobuf error: %s", err) + return nil, err + } + + return sysfw, nil +} + +// Deserialize converts a protocolbuffer structure to byte array. +func (n *Nft) Deserialize(sysfw *protocol.SysFirewall) ([]byte, error) { + jun := jsonpb.Marshaler{ + OrigName: true, + EmitDefaults: true, + Indent: " ", + } + + // NOTE: '<' and '>' characters are encoded to unicode (\u003c). + // This has no effect on adding rules to nftables. + // Users can still write "<" if they want to, rules are added ok. + + var b bytes.Buffer + if err := jun.Marshal(&b, sysfw); err != nil { + log.Error("nfables.Deserialize() error 2: %s", err) + return nil, err + } + return b.Bytes(), nil +} diff --git a/daemon/firewall/nftables/nftest/nftest.go b/daemon/firewall/nftables/nftest/nftest.go new file mode 100644 index 0000000..861b30a --- /dev/null +++ b/daemon/firewall/nftables/nftest/nftest.go @@ -0,0 +1,63 @@ +package nftest + +import ( + "os" + "runtime" + "testing" + + nftb "github.com/evilsocket/opensnitch/daemon/firewall/nftables" + "github.com/google/nftables" + "github.com/vishvananda/netns" +) + +var ( + conn *nftables.Conn + newNS netns.NsHandle + + // Fw represents the nftables Fw object. + Fw, _ = nftb.Fw() +) + +func init() { + nftb.InitMapsStore() +} + +// SkipIfNotPrivileged will skip the test from where it's invoked, +// to skip the test if we don't have root privileges. +// This may occur when executing the tests on restricted environments, +// such as containers, chroots, etc. +func SkipIfNotPrivileged(t *testing.T) { + if os.Getenv("PRIVILEGED_TESTS") == "" { + t.Skip("Set PRIVILEGED_TESTS to 1 to launch these tests, and launch them as root, or as a user allowed to create new namespaces.") + } +} + +// OpenSystemConn opens a new connection with the kernel in a new namespace. +// https://github.com/google/nftables/blob/8f2d395e1089dea4966c483fbeae7e336917c095/internal/nftest/system_conn.go#L15 +func OpenSystemConn(t *testing.T) (*nftables.Conn, netns.NsHandle) { + t.Helper() + // We lock the goroutine into the current thread, as namespace operations + // such as those invoked by `netns.New()` are thread-local. This is undone + // in nftest.CleanupSystemConn(). + runtime.LockOSThread() + + ns, err := netns.New() + if err != nil { + t.Fatalf("netns.New() failed: %v", err) + } + t.Log("OpenSystemConn() with NS:", ns) + c, err := nftables.New(nftables.WithNetNSFd(int(ns))) + if err != nil { + t.Fatalf("nftables.New() failed: %v", err) + } + return c, ns +} + +// CleanupSystemConn closes the given namespace. +func CleanupSystemConn(t *testing.T, newNS netns.NsHandle) { + defer runtime.UnlockOSThread() + + if err := newNS.Close(); err != nil { + t.Fatalf("newNS.Close() failed: %v", err) + } +} diff --git a/daemon/firewall/nftables/nftest/test_utils.go b/daemon/firewall/nftables/nftest/test_utils.go new file mode 100644 index 0000000..10b2a21 --- /dev/null +++ b/daemon/firewall/nftables/nftest/test_utils.go @@ -0,0 +1,202 @@ +package nftest + +import ( + "bytes" + "reflect" + "testing" + + "github.com/evilsocket/opensnitch/daemon/firewall/config" + "github.com/google/nftables" + "github.com/google/nftables/expr" +) + +// TestsT defines the fields of a test. +type TestsT struct { + Name string + Family string + Parms string + Values []*config.ExprValues + ExpectedExprsNum int + ExpectedExprs []interface{} + ExpectedFail bool +} + +// AreExprsValid checks if the expressions defined in the given rule are valid +// according to the expected expressions defined in the tests. +func AreExprsValid(t *testing.T, test *TestsT, rule *nftables.Rule) bool { + + total := len(rule.Exprs) + if total != test.ExpectedExprsNum { + t.Errorf("expected %d expressions, found %d", test.ExpectedExprsNum, total) + return false + } + + for idx, e := range rule.Exprs { + if reflect.TypeOf(e).String() != reflect.TypeOf(test.ExpectedExprs[idx]).String() { + t.Errorf("first expression should be %s, instead of: %s", reflect.TypeOf(test.ExpectedExprs[idx]), reflect.TypeOf(e)) + return false + } + + switch e.(type) { + case *expr.Meta: + lExpr, ok := e.(*expr.Meta) + lExpect, okExpected := test.ExpectedExprs[idx].(*expr.Meta) + if !ok || !okExpected { + t.Errorf("invalid Meta expr,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) + return false + } + if lExpr.Key != lExpect.Key || lExpr.Register != lExpect.Register { + t.Errorf("invalid Meta.Key,\ngot: %+v\nexpected: %+v\n", lExpr.Key, lExpect.Key) + } + if lExpr.SourceRegister != lExpect.SourceRegister { + t.Errorf("invalid Meta.SourceRegister,\ngot: %+v\nexpected: %+v\n", lExpr.SourceRegister, lExpect.SourceRegister) + } + if lExpr.Register != lExpect.Register { + t.Errorf("invalid Meta.Register,\ngot: %+v\nexpected: %+v\n", lExpr.SourceRegister, lExpect.SourceRegister) + } + + case *expr.Immediate: + lExpr, ok := e.(*expr.Immediate) + lExpect, okExpected := test.ExpectedExprs[idx].(*expr.Immediate) + if !ok || !okExpected { + t.Errorf("invalid Immediate expr,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) + return false + } + if !bytes.Equal(lExpr.Data, lExpect.Data) && !test.ExpectedFail { + t.Errorf("invalid Immediate.Data,\ngot: %+v,\nexpected: %+v", lExpr.Data, lExpect.Data) + return false + } + + case *expr.TProxy: + lExpr, ok := e.(*expr.TProxy) + lExpect, okExpected := test.ExpectedExprs[idx].(*expr.TProxy) + if !ok || !okExpected { + t.Errorf("invalid TProxy expr,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) + return false + } + if lExpr.Family != lExpect.Family || lExpr.TableFamily != lExpect.TableFamily || lExpr.RegPort != lExpect.RegPort { + t.Errorf("invalid TProxy expr,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) + return false + } + + case *expr.Redir: + lExpr, ok := e.(*expr.Redir) + lExpect, okExpected := test.ExpectedExprs[idx].(*expr.Redir) + if !ok || !okExpected { + t.Errorf("invalid Redir expr,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) + return false + } + if lExpr.RegisterProtoMin != lExpect.RegisterProtoMin { + t.Errorf("invalid Redir expr,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) + return false + } + + case *expr.Masq: + lExpr, ok := e.(*expr.Masq) + lExpect, okExpected := test.ExpectedExprs[idx].(*expr.Masq) + if !ok || !okExpected { + t.Errorf("invalid Masq expr,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) + return false + } + if lExpr.ToPorts != lExpect.ToPorts || + lExpr.Random != lExpect.Random || + lExpr.FullyRandom != lExpect.FullyRandom || + lExpr.Persistent != lExpect.Persistent { + t.Errorf("invalid Masq expr,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) + return false + } + + case *expr.NAT: + lExpr, ok := e.(*expr.NAT) + lExpect, okExpected := test.ExpectedExprs[idx].(*expr.NAT) + if !ok || !okExpected { + t.Errorf("invalid NAT expr,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) + return false + } + if lExpr.RegProtoMin != lExpect.RegProtoMin || + lExpr.RegAddrMin != lExpect.RegAddrMin || + lExpr.Random != lExpect.Random || + lExpr.FullyRandom != lExpect.FullyRandom || + lExpr.Persistent != lExpect.Persistent { + t.Errorf("invalid NAT expr,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) + return false + } + + case *expr.Quota: + lExpr, ok := e.(*expr.Quota) + lExpect, okExpected := test.ExpectedExprs[idx].(*expr.Quota) + if !ok || !okExpected { + t.Errorf("invalid Quota expr,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) + return false + } + if lExpr.Bytes != lExpect.Bytes || + lExpr.Over != lExpect.Over || + lExpr.Consumed != lExpect.Consumed { + t.Errorf("invalid Quota.Data,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) + return false + } + + case *expr.Ct: + lExpr, ok := e.(*expr.Ct) + lExpect, okExpected := test.ExpectedExprs[idx].(*expr.Ct) + if !ok || !okExpected { + t.Errorf("invalid Ct expr,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) + return false + } + if lExpr.Key != lExpect.Key || lExpr.Register != lExpect.Register || lExpr.SourceRegister != lExpect.SourceRegister { + t.Errorf("invalid Ct parms,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) + return false + } + + case *expr.Bitwise: + lExpr, ok := e.(*expr.Bitwise) + lExpect, okExpected := test.ExpectedExprs[idx].(*expr.Bitwise) + if !ok || !okExpected { + t.Errorf("invalid Bitwise expr,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) + return false + } + if lExpr.Len != lExpect.Len || + !bytes.Equal(lExpr.Mask, lExpect.Mask) || + !bytes.Equal(lExpr.Xor, lExpect.Xor) || + lExpr.DestRegister != lExpect.DestRegister || + lExpr.SourceRegister != lExpect.SourceRegister { + t.Errorf("invalid Bitwise parms,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) + return false + } + + case *expr.Log: + lExpr, ok := e.(*expr.Log) + lExpect, okExpected := test.ExpectedExprs[idx].(*expr.Log) + if !ok || !okExpected { + t.Errorf("invalid Log expr,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) + return false + } + if !bytes.Equal(lExpr.Data, lExpect.Data) && !test.ExpectedFail { + t.Errorf("invalid Log.Data,\ngot: %+v,\nexpected: %+v", lExpr.Data, lExpect.Data) + return false + } + if lExpr.Key != lExpect.Key || + lExpr.Level != lExpect.Level || + lExpr.Group != lExpect.Group || + lExpr.Snaplen != lExpect.Snaplen || + lExpr.QThreshold != lExpect.QThreshold { + t.Errorf("invalid Log fields,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) + return false + } + + case *expr.Cmp: + lExpr, ok := e.(*expr.Cmp) + lExpect, okExpected := test.ExpectedExprs[idx].(*expr.Cmp) + if !ok || !okExpected { + t.Errorf("invalid Cmp expr,\ngot: %+v,\nexpected: %+v", lExpr, lExpect) + return false + } + if !bytes.Equal(lExpr.Data, lExpect.Data) && !test.ExpectedFail { + t.Errorf("invalid Cmp.Data,\ngot: %+v,\nexpected: %+v", lExpr.Data, lExpect.Data) + return false + } + } + } + + return true +} diff --git a/daemon/firewall/nftables/nftest/utils.go b/daemon/firewall/nftables/nftest/utils.go new file mode 100644 index 0000000..7d05138 --- /dev/null +++ b/daemon/firewall/nftables/nftest/utils.go @@ -0,0 +1,117 @@ +package nftest + +import ( + "testing" + + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" + "github.com/google/nftables" + "github.com/google/nftables/expr" +) + +// AddTestRule adds a generic table, chain and rule with the given expression. +func AddTestRule(t *testing.T, conn *nftables.Conn, exp *[]expr.Any) (*nftables.Rule, *nftables.Chain) { + + _, err := Fw.AddTable("yyy", exprs.NFT_FAMILY_INET) + if err != nil { + t.Errorf("pre step add_table() yyy-inet failed: %s", err) + return nil, nil + } + chn := Fw.AddChain( + exprs.NFT_HOOK_INPUT, + "yyy", + exprs.NFT_FAMILY_INET, + nftables.ChainPriorityFilter, + nftables.ChainTypeFilter, + nftables.ChainHookInput, + nftables.ChainPolicyAccept) + if chn == nil { + t.Error("pre step add_chain() input-yyy-inet failed") + return nil, nil + } + //nft.Commit() + + r, err := Fw.AddRule( + exprs.NFT_HOOK_INPUT, "yyy", exprs.NFT_FAMILY_INET, + 0, + "key-yyy", + exp) + if err != nil { + t.Errorf("Error adding rule: %s", err) + return nil, nil + } + t.Logf("Rule: %+v", r) + + return r, chn +} + +// AddTestSNATRule adds a generic table, chain and rule with the given expression. +func AddTestSNATRule(t *testing.T, conn *nftables.Conn, exp *[]expr.Any) (*nftables.Rule, *nftables.Chain) { + + _, err := Fw.AddTable("uuu", exprs.NFT_FAMILY_INET) + if err != nil { + t.Errorf("pre step add_table() uuu-inet failed: %s", err) + return nil, nil + } + chn := Fw.AddChain( + exprs.NFT_HOOK_POSTROUTING, + "uuu", + exprs.NFT_FAMILY_INET, + nftables.ChainPriorityNATSource, + nftables.ChainTypeNAT, + nftables.ChainHookPostrouting, + nftables.ChainPolicyAccept) + if chn == nil { + t.Error("pre step add_chain() input-uuu-inet failed") + return nil, nil + } + //nft.Commit() + + r, err := Fw.AddRule( + exprs.NFT_HOOK_POSTROUTING, "uuu", exprs.NFT_FAMILY_INET, + 0, + "key-uuu", + exp) + if err != nil { + t.Errorf("Error adding rule: %s", err) + return nil, nil + } + t.Logf("Rule: %+v", r) + + return r, chn +} + +// AddTestDNATRule adds a generic table, chain and rule with the given expression. +func AddTestDNATRule(t *testing.T, conn *nftables.Conn, exp *[]expr.Any) (*nftables.Rule, *nftables.Chain) { + + _, err := Fw.AddTable("iii", exprs.NFT_FAMILY_INET) + if err != nil { + t.Errorf("pre step add_table() iii-inet failed: %s", err) + return nil, nil + } + chn := Fw.AddChain( + exprs.NFT_HOOK_PREROUTING, + "iii", + exprs.NFT_FAMILY_INET, + nftables.ChainPriorityNATDest, + nftables.ChainTypeNAT, + nftables.ChainHookPrerouting, + nftables.ChainPolicyAccept) + if chn == nil { + t.Error("pre step add_chain() input-iii-inet failed") + return nil, nil + } + //nft.Commit() + + r, err := Fw.AddRule( + exprs.NFT_HOOK_PREROUTING, "iii", exprs.NFT_FAMILY_INET, + 0, + "key-iii", + exp) + if err != nil { + t.Errorf("Error adding rule: %s", err) + return nil, nil + } + t.Logf("Rule: %+v", r) + + return r, chn +} diff --git a/daemon/firewall/nftables/parser.go b/daemon/firewall/nftables/parser.go new file mode 100644 index 0000000..2605c89 --- /dev/null +++ b/daemon/firewall/nftables/parser.go @@ -0,0 +1,195 @@ +package nftables + +import ( + "github.com/evilsocket/opensnitch/daemon/firewall/config" + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/google/nftables" + "github.com/google/nftables/expr" +) + +// nftables rules are composed of expressions, for example: +// tcp dport 443 ip daddr 192.168.1.1 +// \-----------/ \------------------/ +// with these format: +// keyword1<SPACE>keyword2<SPACE>value... +// +// here we parse the expression, and based on keyword1, we build the rule with the given options. +// +// If the rule has multiple values (tcp dport 80,443,8080), no spaces are allowed, +// and the separator is a ",", instead of the format { 80, 443, 8080 } +// +// In order to debug invalid expressions, or how to build new ones, use the following command: +// # nft --debug netlink add rule filter output mark set 1 +// ip filter output +// [ immediate reg 1 0x00000001 ] +// [ meta set mark with reg 1 ] +// +// Debugging added rules: +// nft --debug netlink list ruleset +// +// https://wiki.archlinux.org/title/Nftables#Expressions +// https://wiki.nftables.org/wiki-nftables/index.php/Building_rules_through_expressions +func (n *Nft) parseExpression(table, chain, family string, expression *config.Expressions) *[]expr.Any { + var exprList []expr.Any + cmpOp := exprs.NewOperator(expression.Statement.Op) + + switch expression.Statement.Name { + + case exprs.NFT_CT: + exprCt := n.buildConntrackRule(expression.Statement.Values, &cmpOp) + if exprCt == nil { + log.Warning("%s Ct statement error", logTag) + return nil + } + exprList = append(exprList, *exprCt...) + + case exprs.NFT_META: + metaExpr, err := exprs.NewExprMeta(expression.Statement.Values, &cmpOp) + if err != nil { + log.Warning("%s meta statement error: %s", logTag, err) + return nil + } + + for _, exprValue := range expression.Statement.Values { + switch exprValue.Key { + case exprs.NFT_META_L4PROTO: + l4rule, err := n.buildL4ProtoRule(table, family, exprValue.Value, &cmpOp) + if err != nil { + log.Warning("%s meta.l4proto statement error: %s", logTag, err) + return nil + } + *metaExpr = append(*metaExpr, *l4rule...) + case exprs.NFT_DPORT, exprs.NFT_SPORT: + exprPDir, err := exprs.NewExprPortDirection(exprValue.Key) + if err != nil { + log.Warning("%s ports statement error: %s", logTag, err) + return nil + } + *metaExpr = append(*metaExpr, []expr.Any{exprPDir}...) + portsRule, err := n.buildPortsRule(table, family, exprValue.Value, &cmpOp) + if err != nil { + log.Warning("%s meta.l4proto.ports statement error: %s", logTag, err) + return nil + } + *metaExpr = append(*metaExpr, *portsRule...) + } + } + return metaExpr + + case exprs.NFT_ETHER: + etherExpr, err := exprs.NewExprEther(expression.Statement.Values) + if err != nil { + log.Warning("%s ether statement error: %s", logTag, err) + return nil + } + return etherExpr + + // TODO: support iif, oif + case exprs.NFT_IIFNAME, exprs.NFT_OIFNAME: + isOut := expression.Statement.Name == exprs.NFT_OIFNAME + iface := expression.Statement.Values[0].Key + if iface == "" { + log.Warning("%s network interface statement error: %s", logTag, expression.Statement.Name) + return nil + } + exprList = append(exprList, *exprs.NewExprIface(iface, isOut, cmpOp)...) + + case exprs.NFT_FAMILY_IP, exprs.NFT_FAMILY_IP6: + exprIP, err := exprs.NewExprIP(family, expression.Statement.Values, cmpOp) + if err != nil { + log.Warning("%s addr statement error: %s", logTag, err) + return nil + } + exprList = append(exprList, *exprIP...) + + case exprs.NFT_PROTO_ICMP, exprs.NFT_PROTO_ICMPv6: + exprICMP := n.buildICMPRule(table, family, expression.Statement.Name, expression.Statement.Values) + if exprICMP == nil { + log.Warning("%s icmp statement error", logTag) + return nil + } + exprList = append(exprList, *exprICMP...) + + case exprs.NFT_LOG: + exprLog, err := exprs.NewExprLog(expression.Statement) + if err != nil { + log.Warning("%s log statement error", logTag) + return nil + } + exprList = append(exprList, *exprLog...) + + case exprs.NFT_LIMIT: + exprLimit, err := exprs.NewExprLimit(expression.Statement) + if err != nil { + log.Warning("%s %s", logTag, err) + return nil + } + exprList = append(exprList, *exprLimit...) + + case exprs.NFT_PROTO_UDP, exprs.NFT_PROTO_TCP, exprs.NFT_PROTO_UDPLITE, exprs.NFT_PROTO_SCTP, exprs.NFT_PROTO_DCCP: + exprProto, err := exprs.NewExprProtocol(expression.Statement.Name) + if err != nil { + log.Warning("%s proto statement error: %s", logTag, err) + return nil + } + exprList = append(exprList, *exprProto...) + + for _, exprValue := range expression.Statement.Values { + + switch exprValue.Key { + case exprs.NFT_DPORT, exprs.NFT_SPORT: + exprPDir, err := exprs.NewExprPortDirection(exprValue.Key) + if err != nil { + log.Warning("%s ports statement error: %s", logTag, err) + return nil + } + exprList = append(exprList, []expr.Any{exprPDir}...) + portsRule, err := n.buildPortsRule(table, family, exprValue.Value, &cmpOp) + if err != nil { + log.Warning("%s proto.ports statement error: %s", logTag, err) + return nil + } + exprList = append(exprList, *portsRule...) + } + + } + + case exprs.NFT_QUOTA: + exprQuota, err := exprs.NewQuota(expression.Statement.Values) + if err != nil { + log.Warning("%s quota statement error: %s", logTag, err) + return nil + } + + exprList = append(exprList, *exprQuota...) + + case exprs.NFT_NOTRACK: + exprList = append(exprList, *exprs.NewNoTrack()...) + + case exprs.NFT_COUNTER: + defaultCounterName := "opensnitch" + counterObj := &nftables.CounterObj{ + Table: &nftables.Table{Name: table, Family: nftables.TableFamilyIPv4}, + Name: defaultCounterName, + Bytes: 0, + Packets: 0, + } + for _, counterOption := range expression.Statement.Values { + switch counterOption.Key { + case exprs.NFT_COUNTER_NAME: + defaultCounterName = counterOption.Value + counterObj.Name = defaultCounterName + case exprs.NFT_COUNTER_BYTES: + // TODO: allow to set initial bytes/packets? + counterObj.Bytes = 1 + case exprs.NFT_COUNTER_PACKETS: + counterObj.Packets = 1 + } + } + n.Conn.AddObj(counterObj) + exprList = append(exprList, *exprs.NewExprCounter(defaultCounterName)...) + } + + return &exprList +} diff --git a/daemon/firewall/nftables/rule_helpers.go b/daemon/firewall/nftables/rule_helpers.go new file mode 100644 index 0000000..158b0f6 --- /dev/null +++ b/daemon/firewall/nftables/rule_helpers.go @@ -0,0 +1,228 @@ +package nftables + +import ( + "fmt" + "strings" + + "github.com/evilsocket/opensnitch/daemon/firewall/config" + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/google/nftables" + "github.com/google/nftables/expr" +) + +// rules examples: https://github.com/google/nftables/blob/master/nftables_test.go + +func (n *Nft) buildICMPRule(table, family string, icmpProtoVersion string, icmpOptions []*config.ExprValues) *[]expr.Any { + tbl := n.GetTable(table, family) + if tbl == nil { + return nil + } + offset := uint32(0) + icmpType := uint8(0) + setType := nftables.SetDatatype{} + + switch icmpProtoVersion { + case exprs.NFT_PROTO_ICMP: + setType = nftables.TypeICMPType + case exprs.NFT_PROTO_ICMPv6: + setType = nftables.TypeICMP6Type + default: + return nil + } + + exprICMP, _ := exprs.NewExprProtocol(icmpProtoVersion) + ICMPrule := []expr.Any{} + ICMPrule = append(ICMPrule, *exprICMP...) + + ICMPtemp := []expr.Any{} + setElements := []nftables.SetElement{} + for _, icmp := range icmpOptions { + switch icmp.Key { + case exprs.NFT_ICMP_TYPE: + icmpTypeList := strings.Split(icmp.Value, ",") + for _, icmpTypeStr := range icmpTypeList { + if exprs.NFT_PROTO_ICMPv6 == icmpProtoVersion { + icmpType = exprs.GetICMPv6Type(icmpTypeStr) + } else { + icmpType = exprs.GetICMPType(icmpTypeStr) + } + exprCmp := &expr.Cmp{ + Op: expr.CmpOpEq, + Register: 1, + Data: []byte{icmpType}, + } + ICMPtemp = append(ICMPtemp, []expr.Any{exprCmp}...) + + // fill setElements. If there're more than 1 icmp type we'll use it later + setElements = append(setElements, + []nftables.SetElement{ + { + Key: []byte{icmpType}, + }, + }...) + } + case exprs.NFT_ICMP_CODE: + // TODO + offset = 1 + } + } + + ICMPrule = append(ICMPrule, []expr.Any{ + &expr.Payload{ + DestRegister: 1, + Base: expr.PayloadBaseTransportHeader, + Offset: offset, // 0 type, 1 code + Len: 1, + }, + }...) + + if len(setElements) == 1 { + ICMPrule = append(ICMPrule, ICMPtemp...) + } else { + set := &nftables.Set{ + Anonymous: true, + Constant: true, + Table: tbl, + KeyType: setType, + } + if err := n.Conn.AddSet(set, setElements); err != nil { + log.Warning("%s AddSet() error: %s", logTag, err) + return nil + } + sysSets = append(sysSets, []*nftables.Set{set}...) + + ICMPrule = append(ICMPrule, []expr.Any{ + &expr.Lookup{ + SourceRegister: 1, + SetName: set.Name, + SetID: set.ID, + }}...) + } + + return &ICMPrule +} + +func (n *Nft) buildConntrackRule(ctOptions []*config.ExprValues, cmpOp *expr.CmpOp) *[]expr.Any { + exprList := []expr.Any{} + + setMark := false + for _, ctOption := range ctOptions { + switch ctOption.Key { + // we expect to have multiple "state" keys: + // { "state": "established", "state": "related" } + case exprs.NFT_CT_STATE: + ctExprState, err := exprs.NewExprCtState(ctOptions) + if err != nil { + log.Warning("%s ct set state error: %s", logTag, err) + return nil + } + exprList = append(exprList, *ctExprState...) + exprList = append(exprList, + &expr.Cmp{Op: expr.CmpOpNeq, Register: 1, Data: []byte{0, 0, 0, 0}}, + ) + // we only need to iterate once here + goto Exit + case exprs.NFT_CT_SET_MARK: + setMark = true + case exprs.NFT_CT_MARK: + ctExprMark, err := exprs.NewExprCtMark(setMark, ctOption.Value, cmpOp) + if err != nil { + log.Warning("%s ct mark error: %s", logTag, err) + return nil + } + exprList = append(exprList, *ctExprMark...) + goto Exit + default: + log.Warning("%s invalid conntrack option: %s", logTag, ctOption) + return nil + } + } + +Exit: + return &exprList +} + +// buildL4ProtoRule helper builds a new protocol rule to match ports and protocols. +// +// nft --debug=netlink add rule filter input meta l4proto { tcp, udp } th dport 53 +// __set%d filter 3 size 2 +// __set%d filter 0 +// element 00000006 : 0 [end] element 00000011 : 0 [end] +// ip filter input +// [ meta load l4proto => reg 1 ] +// [ lookup reg 1 set __set%d ] +// [ payload load 2b @ transport header + 2 => reg 1 ] +// [ cmp eq reg 1 0x00003500 ] +func (n *Nft) buildL4ProtoRule(table, family, l4prots string, cmpOp *expr.CmpOp) (*[]expr.Any, error) { + tbl := n.GetTable(table, family) + if tbl == nil { + return nil, fmt.Errorf("Invalid table (%s, %s)", table, family) + } + exprList := []expr.Any{} + if strings.Index(l4prots, ",") != -1 { + set := &nftables.Set{ + Anonymous: true, + Constant: true, + Table: tbl, + KeyType: nftables.TypeInetProto, + } + protoSet := exprs.NewExprProtoSet(l4prots) + if err := n.Conn.AddSet(set, *protoSet); err != nil { + log.Warning("%s protoSet, AddSet() error: %s", logTag, err) + return nil, err + } + exprList = append(exprList, &expr.Lookup{ + SourceRegister: 1, + SetName: set.Name, + SetID: set.ID, + }) + } else { + exprProto := exprs.NewExprL4Proto(l4prots, cmpOp) + exprList = append(exprList, *exprProto...) + } + + return &exprList, nil +} + +func (n *Nft) buildPortsRule(table, family, ports string, cmpOp *expr.CmpOp) (*[]expr.Any, error) { + tbl := n.GetTable(table, family) + if tbl == nil { + return nil, fmt.Errorf("Invalid table (%s, %s)", table, family) + } + exprList := []expr.Any{} + if strings.Index(ports, ",") != -1 { + set := &nftables.Set{ + Anonymous: true, + Constant: true, + Table: tbl, + KeyType: nftables.TypeInetService, + } + setElements := exprs.NewExprPortSet(ports) + if err := n.Conn.AddSet(set, *setElements); err != nil { + log.Warning("%s portSet, AddSet() error: %s", logTag, err) + return nil, err + } + exprList = append(exprList, &expr.Lookup{ + SourceRegister: 1, + SetName: set.Name, + SetID: set.ID, + }) + sysSets = append(sysSets, []*nftables.Set{set}...) + } else if strings.Index(ports, "-") != -1 { + portRange, err := exprs.NewExprPortRange(ports, cmpOp) + if err != nil { + log.Warning("%s invalid portRange: %s, %s", logTag, ports, err) + return nil, err + } + exprList = append(exprList, *portRange...) + } else { + exprPort, err := exprs.NewExprPort(ports, cmpOp) + if err != nil { + return nil, err + } + exprList = append(exprList, *exprPort...) + } + + return &exprList, nil +} diff --git a/daemon/firewall/nftables/rules.go b/daemon/firewall/nftables/rules.go new file mode 100644 index 0000000..fdd79d9 --- /dev/null +++ b/daemon/firewall/nftables/rules.go @@ -0,0 +1,283 @@ +package nftables + +import ( + "fmt" + + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/google/nftables" + "github.com/google/nftables/binaryutil" + "github.com/google/nftables/expr" + "golang.org/x/sys/unix" +) + +// QueueDNSResponses redirects DNS responses to us, in order to keep a cache +// of resolved domains. +// This rule must be added in top of the system rules, otherwise it may get bypassed. +// nft insert rule ip filter input udp sport 53 queue num 0 bypass +func (n *Nft) QueueDNSResponses(enable bool, logError bool) (error, error) { + if n.Conn == nil { + return nil, nil + } + families := []string{exprs.NFT_FAMILY_INET} + for _, fam := range families { + table := n.GetTable(exprs.NFT_CHAIN_FILTER, fam) + chain := GetChain(exprs.NFT_HOOK_INPUT, table) + if table == nil { + log.Error("QueueDNSResponses() Error getting table: %s-filter", fam) + continue + } + if chain == nil { + log.Error("QueueDNSResponses() Error getting chain: %s-%d", table.Name, table.Family) + continue + } + + // nft list ruleset -a + n.Conn.InsertRule(&nftables.Rule{ + Position: 0, + Table: table, + Chain: chain, + Exprs: []expr.Any{ + &expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1}, + &expr.Cmp{ + Op: expr.CmpOpEq, + Register: 1, + Data: []byte{unix.IPPROTO_UDP}, + }, + &expr.Payload{ + DestRegister: 1, + Base: expr.PayloadBaseTransportHeader, + Offset: 0, + Len: 2, + }, + &expr.Cmp{ + Op: expr.CmpOpEq, + Register: 1, + Data: binaryutil.BigEndian.PutUint16(uint16(53)), + }, + &expr.Queue{ + Num: n.QueueNum, + Flag: expr.QueueFlagBypass, + }, + }, + // rule key, to allow get it later by key + UserData: []byte(InterceptionRuleKey), + }) + } + // apply changes + if !n.Commit() { + return fmt.Errorf("Error adding DNS interception rules"), nil + } + + return nil, nil +} + +// QueueConnections inserts the firewall rule which redirects connections to us. +// Connections are queued until the user denies/accept them, or reaches a timeout. +// This rule must be added at the end of all the other rules, that way we can add +// rules above this one to exclude a service/app from being intercepted. +// nft insert rule ip mangle OUTPUT ct state new queue num 0 bypass +func (n *Nft) QueueConnections(enable bool, logError bool) (error, error) { + if n.Conn == nil { + return nil, fmt.Errorf("nftables QueueConnections: netlink connection not active") + } + table := n.GetTable(exprs.NFT_CHAIN_MANGLE, exprs.NFT_FAMILY_INET) + if table == nil { + return nil, fmt.Errorf("QueueConnections() Error getting table mangle-inet") + } + chain := GetChain(exprs.NFT_HOOK_OUTPUT, table) + if chain == nil { + return nil, fmt.Errorf("QueueConnections() Error getting outputChain: output-%s", table.Name) + } + + n.Conn.AddRule(&nftables.Rule{ + Position: 0, + Table: table, + Chain: chain, + Exprs: []expr.Any{ + &expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1}, + &expr.Cmp{ + Op: expr.CmpOpNeq, + Register: 1, + Data: []byte{unix.IPPROTO_TCP}, + }, + &expr.Ct{Register: 1, SourceRegister: false, Key: expr.CtKeySTATE}, + &expr.Bitwise{ + SourceRegister: 1, + DestRegister: 1, + Len: 4, + Mask: binaryutil.NativeEndian.PutUint32(expr.CtStateBitNEW | expr.CtStateBitRELATED), + Xor: binaryutil.NativeEndian.PutUint32(0), + }, + &expr.Cmp{Op: expr.CmpOpNeq, Register: 1, Data: []byte{0, 0, 0, 0}}, + &expr.Queue{ + Num: n.QueueNum, + Flag: expr.QueueFlagBypass, + }, + }, + // rule key, to allow get it later by key + UserData: []byte(InterceptionRuleKey), + }) + + /* nft --debug=netlink add rule inet mangle output tcp flags '& (fin|syn|rst|ack) == syn' queue bypass num 0 + [ meta load l4proto => reg 1 ] + [ cmp eq reg 1 0x00000006 ] + [ payload load 1b @ transport header + 13 => reg 1 ] + [ bitwise reg 1 = ( reg 1 & 0x00000002 ) ^ 0x00000000 ] + [ cmp neq reg 1 0x00000000 ] + [ queue num 0 bypass ] + + Intercept packets *only* with the SYN flag set. + Using 'ct state NEW' causes to intercept packets with other flags set, which + sometimes means that we receive outbound connections not in the expected order: + 443:1.1.1.1 -> 192.168.123:12345 (bits ACK, ACK+PSH or SYN+ACK set) + */ + n.Conn.AddRule(&nftables.Rule{ + Position: 0, + Table: table, + Chain: chain, + Exprs: []expr.Any{ + &expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1}, + &expr.Cmp{ + Op: expr.CmpOpEq, + Register: 1, + Data: []byte{unix.IPPROTO_TCP}, + }, + &expr.Payload{ + DestRegister: 1, + Base: expr.PayloadBaseTransportHeader, + Offset: 13, + Len: 1, + }, + &expr.Bitwise{ + DestRegister: 1, + SourceRegister: 1, + Len: 1, + Mask: []byte{0x17}, + Xor: []byte{0x00}, + }, + &expr.Cmp{ + Op: expr.CmpOpEq, + Register: 1, + Data: []byte{0x02}, + }, + &expr.Queue{ + Num: n.QueueNum, + Flag: expr.QueueFlagBypass, + }, + }, + // rule key, to allow get it later by key + UserData: []byte(InterceptionRuleKey), + }) + + // apply changes + if !n.Commit() { + return fmt.Errorf("Error adding interception rule "), nil + } + + return nil, nil +} + +// InsertRule inserts a rule at the top of rules list. +func (n *Nft) InsertRule(chain, table, family string, position uint64, exprs *[]expr.Any) error { + tbl := n.GetTable(table, family) + if tbl == nil { + return fmt.Errorf("%s getting table: %s, %s", logTag, table, family) + } + + chainKey := getChainKey(chain, tbl) + chn, chok := sysChains.Load(chainKey) + if !chok { + return fmt.Errorf("%s getting table: %s, %s", logTag, table, family) + } + + rule := &nftables.Rule{ + Position: position, + Table: tbl, + Chain: chn.(*nftables.Chain), + Exprs: *exprs, + UserData: []byte(SystemRuleKey), + } + n.Conn.InsertRule(rule) + if !n.Commit() { + return fmt.Errorf("rule not added") + } + + return nil +} + +// AddRule adds a rule to the system. +func (n *Nft) AddRule(chain, table, family string, position uint64, key string, exprs *[]expr.Any) (*nftables.Rule, error) { + tbl := n.GetTable(table, family) + if tbl == nil { + return nil, fmt.Errorf("getting %s table: %s, %s", logTag, table, family) + } + + chainKey := getChainKey(chain, tbl) + chn, chok := sysChains.Load(chainKey) + if !chok { + return nil, fmt.Errorf("getting table: %s, %s", table, family) + } + + rule := &nftables.Rule{ + Position: position, + Table: tbl, + Chain: chn.(*nftables.Chain), + Exprs: *exprs, + UserData: []byte(key), + } + n.Conn.AddRule(rule) + if !n.Commit() { + return nil, fmt.Errorf("adding %s rule", logTag) + } + + return rule, nil +} + +func (n *Nft) delRulesByKey(key string) error { + chains, err := n.Conn.ListChains() + if err != nil { + return fmt.Errorf("error listing nftables chains (%s): %s", key, err) + } + for _, c := range chains { + rules, err := n.Conn.GetRule(c.Table, c) + if err != nil { + log.Warning("Error listing rules (%s): %s", key, err) + continue + } + delRules := 0 + for _, r := range rules { + if string(r.UserData) != key { + continue + } + // just passing the r object doesn't work. + if err := n.Conn.DelRule(&nftables.Rule{ + Table: c.Table, + Chain: c, + Handle: r.Handle, + }); err != nil { + log.Warning("[nftables] error deleting rule (%s): %s", key, err) + continue + } + delRules++ + } + if delRules > 0 { + if !n.Commit() { + log.Warning("%s error deleting rules: %s", logTag, err) + } + } + if len(rules) == 0 || len(rules) == delRules { + _, chfound := sysChains.Load(getChainKey(c.Name, c.Table)) + if chfound { + n.DelChain(c) + } + } + } + + return nil +} + +// DelInterceptionRules deletes our interception rules, by key. +func (n *Nft) DelInterceptionRules() { + n.delRulesByKey(InterceptionRuleKey) +} diff --git a/daemon/firewall/nftables/rules_test.go b/daemon/firewall/nftables/rules_test.go new file mode 100644 index 0000000..e089909 --- /dev/null +++ b/daemon/firewall/nftables/rules_test.go @@ -0,0 +1,215 @@ +package nftables_test + +import ( + "testing" + + nftb "github.com/evilsocket/opensnitch/daemon/firewall/nftables" + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/nftest" + "github.com/google/nftables" +) + +func getRulesList(t *testing.T, conn *nftables.Conn, family, tblName, chnName string) ([]*nftables.Rule, int) { + chains, err := conn.ListChains() + if err != nil { + return nil, -1 + } + + for rdx, c := range chains { + if c.Table.Family == nftb.GetFamilyCode(family) && c.Table.Name == tblName && c.Name == chnName { + rules, err := conn.GetRule(c.Table, c) + if err != nil { + return nil, -1 + } + return rules, rdx + } + } + + return nil, -1 +} + +func getRule(t *testing.T, conn *nftables.Conn, tblName, chnName, key string, ruleHandle uint64) (*nftables.Rule, int) { + chains, err := conn.ListChains() + if err != nil { + return nil, -1 + } + + for _, c := range chains { + rules, err := conn.GetRule(c.Table, c) + if err != nil { + continue + } + for rdx, r := range rules { + //t.Logf("Table: %s<->%s, Chain: %s<->%s, Rule Handle: %d<->%d, UserData: %s<->%s", c.Table.Name, tblName, c.Name, chnName, r.Handle, ruleHandle, string(r.UserData), key) + if c.Table.Name == tblName && c.Name == chnName { + if ruleHandle > 0 && r.Handle == ruleHandle { + return r, rdx + } + if key != "" && string(r.UserData) == key { + return r, rdx + } + } + } + } + + return nil, -1 +} + +func TestAddRule(t *testing.T) { + nftest.SkipIfNotPrivileged(t) + + conn, newNS := nftest.OpenSystemConn(t) + defer nftest.CleanupSystemConn(t, newNS) + nftest.Fw.Conn = conn + + r, chn := nftest.AddTestRule(t, conn, exprs.NewNoTrack()) + + /* + _, err := nft.AddTable("yyy", exprs.NFT_FAMILY_INET) + if err != nil { + t.Error("pre step add_table() yyy-inet failed") + } + chn := nft.AddChain( + exprs.NFT_HOOK_INPUT, + "yyy", + exprs.NFT_FAMILY_INET, + nftables.ChainPriorityFilter, + nftables.ChainTypeFilter, + nftables.ChainHookInput, + nftables.ChainPolicyAccept) + if chn == nil { + t.Error("pre step add_chain() input-yyy-inet failed") + } + + r, err := nft.addRule( + exprs.NFT_HOOK_INPUT, "yyy", exprs.NFT_FAMILY_INET, + 0, + "key-yyy", + exprs.NewNoTrack()) + if err != nil { + t.Errorf("Error adding rule: %s", err) + } + */ + + rules, err := conn.GetRules(chn.Table, chn) + if err != nil || len(rules) != 1 { + t.Errorf("Rule not added, total: %d", len(rules)) + } + t.Log(r.Handle) +} + +func TestInsertRule(t *testing.T) { + nftest.SkipIfNotPrivileged(t) + + conn, newNS := nftest.OpenSystemConn(t) + defer nftest.CleanupSystemConn(t, newNS) + nftest.Fw.Conn = conn + + _, err := nftest.Fw.AddTable("yyy", exprs.NFT_FAMILY_INET) + if err != nil { + t.Error("pre step add_table() yyy-inet failed") + } + chn := nftest.Fw.AddChain( + exprs.NFT_HOOK_INPUT, + "yyy", + exprs.NFT_FAMILY_INET, + nftables.ChainPriorityFilter, + nftables.ChainTypeFilter, + nftables.ChainHookInput, + nftables.ChainPolicyAccept) + if chn == nil { + t.Error("pre step add_chain() input-yyy-inet failed") + } + + err = nftest.Fw.InsertRule( + exprs.NFT_HOOK_INPUT, "yyy", exprs.NFT_FAMILY_INET, + 0, + exprs.NewNoTrack()) + if err != nil { + t.Errorf("Error inserting rule: %s", err) + } + rules, err := conn.GetRules(chn.Table, chn) + if err != nil || len(rules) != 1 { + t.Errorf("Rule not inserted, total: %d", len(rules)) + } +} + +func TestQueueConnections(t *testing.T) { + nftest.SkipIfNotPrivileged(t) + + conn, newNS := nftest.OpenSystemConn(t) + defer nftest.CleanupSystemConn(t, newNS) + nftest.Fw.Conn = conn + + _, err := nftest.Fw.AddTable(exprs.NFT_CHAIN_MANGLE, exprs.NFT_FAMILY_INET) + if err != nil { + t.Error("pre step add_table() mangle-inet failed") + } + chn := nftest.Fw.AddChain( + exprs.NFT_HOOK_OUTPUT, exprs.NFT_CHAIN_MANGLE, exprs.NFT_FAMILY_INET, + nftables.ChainPriorityFilter, + nftables.ChainTypeFilter, + nftables.ChainHookInput, + nftables.ChainPolicyAccept) + if chn == nil { + t.Error("pre step add_chain() output-mangle-inet failed") + } + + if err1, err2 := nftest.Fw.QueueConnections(true, true); err1 != nil && err2 != nil { + t.Errorf("rule to queue connections not added: %s, %s", err1, err2) + } + + r, _ := getRule(t, conn, exprs.NFT_CHAIN_MANGLE, exprs.NFT_HOOK_OUTPUT, nftb.InterceptionRuleKey, 0) + if r == nil { + t.Error("rule to queue connections not in the list") + } + if string(r.UserData) != nftb.InterceptionRuleKey { + t.Errorf("invalid UserData: %s", string(r.UserData)) + } +} + +func TestQueueDNSResponses(t *testing.T) { + nftest.SkipIfNotPrivileged(t) + + conn, newNS := nftest.OpenSystemConn(t) + defer nftest.CleanupSystemConn(t, newNS) + nftest.Fw.Conn = conn + + _, err := nftest.Fw.AddTable(exprs.NFT_CHAIN_FILTER, exprs.NFT_FAMILY_INET) + if err != nil { + t.Error("pre step add_table() filter-inet failed") + } + chn := nftest.Fw.AddChain( + exprs.NFT_HOOK_INPUT, exprs.NFT_CHAIN_FILTER, exprs.NFT_FAMILY_INET, + nftables.ChainPriorityFilter, + nftables.ChainTypeFilter, + nftables.ChainHookInput, + nftables.ChainPolicyAccept) + if chn == nil { + t.Error("pre step add_chain() input-filter-inet failed") + } + + if err1, err2 := nftest.Fw.QueueDNSResponses(true, true); err1 != nil && err2 != nil { + t.Errorf("rule to queue DNS responses not added: %s, %s", err1, err2) + } + + r, _ := getRule(t, conn, exprs.NFT_CHAIN_FILTER, exprs.NFT_HOOK_INPUT, nftb.InterceptionRuleKey, 0) + if r == nil { + t.Error("rule to queue DNS responses not in the list") + } + if string(r.UserData) != nftb.InterceptionRuleKey { + t.Errorf("invalid UserData: %s", string(r.UserData)) + } + + // nftables.DelRule() does not accept rule handles == 0 + // https://github.com/google/nftables/blob/8f2d395e1089dea4966c483fbeae7e336917c095/rule.go#L200 + // sometimes when adding this rule in new namespaces it's added with rule.Handle == 0, so it fails deleting the rule, thus failing the test. + // can it happen on "prod" environments? + /*if err1, err2 := nft.QueueDNSResponses(false, true); err1 != nil && err2 != nil { + t.Errorf("rule to queue DNS responses not deleted: %s, %s", err1, err2) + } + r, _ = getRule(t, conn, exprs.NFT_CHAIN_FILTER, exprs.NFT_HOOK_INPUT, nftb.InterceptionRuleKey, 0) + if r != nil { + t.Error("rule to queue DNS responses should have been deleted") + }*/ +} diff --git a/daemon/firewall/nftables/system.go b/daemon/firewall/nftables/system.go new file mode 100644 index 0000000..08c08a2 --- /dev/null +++ b/daemon/firewall/nftables/system.go @@ -0,0 +1,180 @@ +package nftables + +import ( + "fmt" + "strings" + "sync" + + "github.com/evilsocket/opensnitch/daemon/firewall/config" + "github.com/evilsocket/opensnitch/daemon/firewall/iptables" + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/google/nftables" + "github.com/google/nftables/expr" + "github.com/google/uuid" +) + +// store of tables added to the system +type sysTablesT struct { + tables map[string]*nftables.Table + sync.RWMutex +} + +func (t *sysTablesT) Add(name string, tbl *nftables.Table) { + t.Lock() + defer t.Unlock() + t.tables[name] = tbl +} + +func (t *sysTablesT) Get(name string) *nftables.Table { + t.RLock() + defer t.RUnlock() + return t.tables[name] +} + +func (t *sysTablesT) List() map[string]*nftables.Table { + t.RLock() + defer t.RUnlock() + return t.tables +} + +func (t *sysTablesT) Del(name string) { + t.Lock() + defer t.Unlock() + delete(t.tables, name) +} + +var ( + logTag = "nftables:" + sysTables *sysTablesT + sysChains *sync.Map + origSysChains map[string]*nftables.Chain + sysSets []*nftables.Set +) + +// InitMapsStore initializes internal stores of chains and maps. +func InitMapsStore() { + sysTables = &sysTablesT{ + tables: make(map[string]*nftables.Table), + } + sysChains = &sync.Map{} + origSysChains = make(map[string]*nftables.Chain) +} + +// CreateSystemRule create the custom firewall chains and adds them to system. +// nft insert rule ip opensnitch-filter opensnitch-input udp dport 1153 +func (n *Nft) CreateSystemRule(chain *config.FwChain, logErrors bool) bool { + if chain.IsInvalid() { + log.Warning("%s CreateSystemRule(), Chain's field Name and Family cannot be empty", logTag) + return false + } + + tableName := chain.Table + n.AddTable(chain.Table, chain.Family) + + // regular chains doesn't have a hook, nor a type + if chain.Hook == "" && chain.Type == "" { + n.addRegularChain(chain.Name, tableName, chain.Family) + return n.Commit() + } + + chainPolicy := nftables.ChainPolicyAccept + if iptables.Action(strings.ToLower(chain.Policy)) == exprs.VERDICT_DROP { + chainPolicy = nftables.ChainPolicyDrop + } + + chainHook := GetHook(chain.Hook) + chainPrio, chainType := GetChainPriority(chain.Family, chain.Type, chain.Hook) + if chainPrio == nil { + log.Warning("%s Invalid system firewall combination: %s, %s", logTag, chain.Type, chain.Hook) + return false + } + + if ret := n.AddChain(chain.Name, chain.Table, chain.Family, chainPrio, + chainType, chainHook, chainPolicy); ret == nil { + log.Warning("%s error adding chain: %s, table: %s", logTag, chain.Name, chain.Table) + return false + } + + return n.Commit() +} + +// AddSystemRules creates the system firewall from configuration. +func (n *Nft) AddSystemRules(reload, backupExistingChains bool) { + n.SysConfig.RLock() + defer n.SysConfig.RUnlock() + + if n.SysConfig.Enabled == false { + log.Important("[nftables] AddSystemRules() fw disabled") + return + } + if backupExistingChains { + n.backupExistingChains() + } + + for _, fwCfg := range n.SysConfig.SystemRules { + for _, chain := range fwCfg.Chains { + if !n.CreateSystemRule(chain, true) { + log.Info("createSystem failed: %s %s", chain.Name, chain.Table) + continue + } + for i := len(chain.Rules) - 1; i >= 0; i-- { + if chain.Rules[i].UUID == "" { + uuid := uuid.New() + chain.Rules[i].UUID = uuid.String() + } + if chain.Rules[i].Enabled { + if err4, _ := n.AddSystemRule(chain.Rules[i], chain); err4 != nil { + n.SendError(fmt.Sprintf("%s (%s)", err4, chain.Rules[i].UUID)) + } + } + } + } + } +} + +// DeleteSystemRules deletes the system rules. +// If force is false and the rule has not been previously added, +// it won't try to delete the tables and chains. Otherwise it'll try to delete them. +func (n *Nft) DeleteSystemRules(force, restoreExistingChains, logErrors bool) { + n.Lock() + defer n.Unlock() + + if err := n.delRulesByKey(SystemRuleKey); err != nil { + log.Warning("error deleting interception rules: %s", err) + } + + if restoreExistingChains { + n.restoreBackupChains() + } + if force { + n.DelSystemTables() + } +} + +// AddSystemRule inserts a new rule. +func (n *Nft) AddSystemRule(rule *config.FwRule, chain *config.FwChain) (err4, err6 error) { + n.Lock() + defer n.Unlock() + exprList := []expr.Any{} + + for _, expression := range rule.Expressions { + exprsOfRule := n.parseExpression(chain.Table, chain.Name, chain.Family, expression) + if exprsOfRule == nil { + return fmt.Errorf("%s invalid rule parameters: %v", rule.UUID, expression), nil + } + exprList = append(exprList, *exprsOfRule...) + } + if len(exprList) > 0 { + exprVerdict := exprs.NewExprVerdict(rule.Target, rule.TargetParameters) + if exprVerdict == nil { + return fmt.Errorf("%s invalid verdict %s %s", rule.UUID, rule.Target, rule.TargetParameters), nil + } + exprList = append(exprList, *exprVerdict...) + if err := n.InsertRule(chain.Name, chain.Table, chain.Family, rule.Position, &exprList); err != nil { + return err, nil + } + } + + return nil, nil +} diff --git a/daemon/firewall/nftables/system_test.go b/daemon/firewall/nftables/system_test.go new file mode 100644 index 0000000..59d2f01 --- /dev/null +++ b/daemon/firewall/nftables/system_test.go @@ -0,0 +1,167 @@ +package nftables_test + +import ( + "testing" + + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/nftest" +) + +type sysChainsListT struct { + family string + table string + chain string + expectedRules int +} + +func TestAddSystemRules(t *testing.T) { + nftest.SkipIfNotPrivileged(t) + + conn, newNS := nftest.OpenSystemConn(t) + defer nftest.CleanupSystemConn(t, newNS) + nftest.Fw.Conn = conn + + cfg, err := nftest.Fw.NewSystemFwConfig(nftest.Fw.PreloadConfCallback, nftest.Fw.ReloadConfCallback) + if err != nil { + t.Logf("Error creating fw config: %s", err) + } + + cfg.SetFile("./testdata/test-sysfw-conf.json") + if err := cfg.LoadDiskConfiguration(false); err != nil { + t.Errorf("Error loading config from disk: %s", err) + } + + nftest.Fw.AddSystemRules(false, false) + + rules, _ := getRulesList(t, conn, exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_FILTER, exprs.NFT_HOOK_INPUT) + // 3 rules in total, 1 disabled. + if len(rules) != 1 { + t.Errorf("test-load-conf.json mangle-output should contain only 3 rules, no -> %d", len(rules)) + for _, r := range rules { + t.Logf("%+v", r) + } + } + + rules, _ = getRulesList(t, conn, exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_MANGLE, exprs.NFT_HOOK_OUTPUT) + // 3 rules in total, 1 disabled. + if len(rules) != 3 { + t.Errorf("test-load-conf.json mangle-output should contain only 3 rules, no -> %d", len(rules)) + for _, r := range rules { + t.Log(r) + } + } + + rules, _ = getRulesList(t, conn, exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_MANGLE, exprs.NFT_HOOK_FORWARD) + // 3 rules in total, 1 disabled. + if len(rules) != 1 { + t.Errorf("test-load-conf.json mangle-output should contain only 3 rules, no -> %d", len(rules)) + for _, r := range rules { + t.Log(r) + } + } + +} + +func TestFwConfDisabled(t *testing.T) { + nftest.SkipIfNotPrivileged(t) + + conn, newNS := nftest.OpenSystemConn(t) + defer nftest.CleanupSystemConn(t, newNS) + nftest.Fw.Conn = conn + + cfg, err := nftest.Fw.NewSystemFwConfig(nftest.Fw.PreloadConfCallback, nftest.Fw.ReloadConfCallback) + if err != nil { + t.Logf("Error creating fw config: %s", err) + } + + cfg.SetFile("./testdata/test-sysfw-conf.json") + if err := cfg.LoadDiskConfiguration(false); err != nil { + t.Errorf("Error loading config from disk: %s", err) + } + + nftest.Fw.AddSystemRules(false, false) + + tests := []sysChainsListT{ + { + exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_MANGLE, exprs.NFT_HOOK_OUTPUT, 3, + }, + { + exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_MANGLE, exprs.NFT_HOOK_FORWARD, 1, + }, + { + exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_FILTER, exprs.NFT_HOOK_INPUT, 1, + }, + } + + for _, tt := range tests { + rules, _ := getRulesList(t, conn, tt.family, tt.table, tt.chain) + if len(rules) != 0 { + t.Logf("%d rules found, there should be 0", len(rules)) + } + } +} + +func TestDeleteSystemRules(t *testing.T) { + nftest.SkipIfNotPrivileged(t) + + conn, newNS := nftest.OpenSystemConn(t) + defer nftest.CleanupSystemConn(t, newNS) + nftest.Fw.Conn = conn + + cfg, err := nftest.Fw.NewSystemFwConfig(nftest.Fw.PreloadConfCallback, nftest.Fw.ReloadConfCallback) + if err != nil { + t.Logf("Error creating fw config: %s", err) + } + + cfg.SetFile("./testdata/test-sysfw-conf.json") + if err := cfg.LoadDiskConfiguration(false); err != nil { + t.Errorf("Error loading config from disk: %s", err) + } + + nftest.Fw.AddSystemRules(false, false) + + tests := []sysChainsListT{ + { + exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_MANGLE, exprs.NFT_HOOK_OUTPUT, 3, + }, + { + exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_MANGLE, exprs.NFT_HOOK_FORWARD, 1, + }, + { + exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_FILTER, exprs.NFT_HOOK_INPUT, 1, + }, + } + for _, tt := range tests { + rules, _ := getRulesList(t, conn, tt.family, tt.table, tt.chain) + if len(rules) != tt.expectedRules { + t.Errorf("%d rules found, there should be %d", len(rules), tt.expectedRules) + } + } + + t.Run("test-delete-system-rules", func(t *testing.T) { + nftest.Fw.DeleteSystemRules(false, false, true) + for _, tt := range tests { + rules, _ := getRulesList(t, conn, tt.family, tt.table, tt.chain) + if len(rules) != 0 { + t.Errorf("%d rules found, there should be 0", len(rules)) + } + + tbl := nftest.Fw.GetTable(tt.table, tt.family) + if tbl == nil { + t.Errorf("table %s-%s should exist", tt.table, tt.family) + } + + /*chn := nft.getChain(tt.chain, tbl, tt.family) + if chn == nil { + if chains, err := conn.ListChains(); err == nil { + for _, c := range chains { + } + } + t.Errorf("chain %s-%s-%s should exist", tt.family, tt.table, tt.chain) + }*/ + } + + }) + t.Run("test-delete-system-rules+chains", func(t *testing.T) { + }) +} diff --git a/daemon/firewall/nftables/tables.go b/daemon/firewall/nftables/tables.go new file mode 100644 index 0000000..e613a37 --- /dev/null +++ b/daemon/firewall/nftables/tables.go @@ -0,0 +1,90 @@ +package nftables + +import ( + "fmt" + + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/google/nftables" +) + +// AddTable adds a new table to nftables. +func (n *Nft) AddTable(name, family string) (*nftables.Table, error) { + famCode := GetFamilyCode(family) + tbl := &nftables.Table{ + Family: famCode, + Name: name, + } + n.Conn.AddTable(tbl) + + if !n.Commit() { + return nil, fmt.Errorf("%s error adding system firewall table: %s, family: %s (%d)", logTag, name, family, famCode) + } + key := getTableKey(name, family) + sysTables.Add(key, tbl) + return tbl, nil +} + +// GetTable retrieves an already added table to the system. +func (n *Nft) GetTable(name, family string) *nftables.Table { + return sysTables.Get(getTableKey(name, family)) +} + +func getTableKey(name string, family interface{}) string { + return fmt.Sprint(name, "-", family) +} + +// AddInterceptionTables adds the needed tables to intercept traffic. +func (n *Nft) AddInterceptionTables() error { + if _, err := n.AddTable(exprs.NFT_CHAIN_MANGLE, exprs.NFT_FAMILY_INET); err != nil { + return err + } + if _, err := n.AddTable(exprs.NFT_CHAIN_FILTER, exprs.NFT_FAMILY_INET); err != nil { + return err + } + return nil +} + +// Contrary to iptables, in nftables there're no predefined rules. +// Convention is though to use the iptables names by default. +// We need at least: mangle and filter tables, inet family (IPv4 and IPv6). +func (n *Nft) addSystemTables() { + n.AddTable(exprs.NFT_CHAIN_MANGLE, exprs.NFT_FAMILY_INET) + n.AddTable(exprs.NFT_CHAIN_FILTER, exprs.NFT_FAMILY_INET) +} + +// return the number of rules that we didn't add. +func (n *Nft) nonSystemRules(tbl *nftables.Table) int { + chains, err := n.Conn.ListChains() + if err != nil { + return -1 + } + t := 0 + for _, c := range chains { + if tbl.Name != c.Table.Name && tbl.Family != c.Table.Family { + continue + } + rules, err := n.Conn.GetRule(c.Table, c) + if err != nil { + return -1 + } + t += len(rules) + } + + return t +} + +// DelSystemTables deletes tables created from fw configuration. +func (n *Nft) DelSystemTables() { + for k, tbl := range sysTables.List() { + if n.nonSystemRules(tbl) != 0 { + continue + } + n.Conn.DelTable(tbl) + if !n.Commit() { + log.Warning("error deleting system table: %s", k) + continue + } + sysTables.Del(k) + } +} diff --git a/daemon/firewall/nftables/tables_test.go b/daemon/firewall/nftables/tables_test.go new file mode 100644 index 0000000..36588c8 --- /dev/null +++ b/daemon/firewall/nftables/tables_test.go @@ -0,0 +1,113 @@ +package nftables_test + +import ( + "testing" + + nftb "github.com/evilsocket/opensnitch/daemon/firewall/nftables" + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/nftest" + "github.com/google/nftables" +) + +func tableExists(t *testing.T, conn *nftables.Conn, origtbl *nftables.Table, family string) bool { + tables, err := conn.ListTablesOfFamily( + nftb.GetFamilyCode(family), + ) + if err != nil { + return false + } + found := false + for _, tbl := range tables { + if origtbl != nil && tbl.Name == origtbl.Name { + found = true + break + } + } + return found +} + +func TestAddTable(t *testing.T) { + nftest.SkipIfNotPrivileged(t) + + conn, newNS := nftest.OpenSystemConn(t) + defer nftest.CleanupSystemConn(t, newNS) + nftest.Fw.Conn = conn + + t.Run("inet family", func(t *testing.T) { + tblxxx, err := nftest.Fw.AddTable("xxx", exprs.NFT_FAMILY_INET) + if err != nil { + t.Error("table xxx-inet not added:", err) + } + if tableExists(t, nftest.Fw.Conn, tblxxx, exprs.NFT_FAMILY_INET) == false { + t.Error("table xxx-inet not in the list") + } + + nftest.Fw.DelSystemTables() + if tableExists(t, nftest.Fw.Conn, tblxxx, exprs.NFT_FAMILY_INET) { + t.Error("table xxx-inet still exists") + } + }) + + t.Run("ip family", func(t *testing.T) { + tblxxx, err := nftest.Fw.AddTable("xxx", exprs.NFT_FAMILY_IP) + if err != nil { + t.Error("table xxx-ip not added:", err) + } + if tableExists(t, nftest.Fw.Conn, tblxxx, exprs.NFT_FAMILY_IP) == false { + t.Error("table xxx-ip not in the list") + } + + nftest.Fw.DelSystemTables() + if tableExists(t, nftest.Fw.Conn, tblxxx, exprs.NFT_FAMILY_IP) { + t.Errorf("table xxx-ip still exists:") // %+v", sysTables) + } + }) + + t.Run("ip6 family", func(t *testing.T) { + tblxxx, err := nftest.Fw.AddTable("xxx", exprs.NFT_FAMILY_IP6) + if err != nil { + t.Error("table xxx-ip6 not added:", err) + } + if tableExists(t, nftest.Fw.Conn, tblxxx, exprs.NFT_FAMILY_IP6) == false { + t.Error("table xxx-ip6 not in the list") + } + + nftest.Fw.DelSystemTables() + if tableExists(t, nftest.Fw.Conn, tblxxx, exprs.NFT_FAMILY_IP6) { + t.Errorf("table xxx-ip6 still exists:") // %+v", sysTables) + } + }) +} + +// TestAddInterceptionTables checks if the needed tables have been created. +// We use 2: mangle-inet for intercepting outbound connections, and filter-inet for DNS responses interception +func TestAddInterceptionTables(t *testing.T) { + nftest.SkipIfNotPrivileged(t) + + conn, newNS := nftest.OpenSystemConn(t) + defer nftest.CleanupSystemConn(t, newNS) + nftest.Fw.Conn = conn + + if err := nftest.Fw.AddInterceptionTables(); err != nil { + t.Errorf("addInterceptionTables() error: %s", err) + } + + t.Run("mangle-inet", func(t *testing.T) { + tblmangle := nftest.Fw.GetTable(exprs.NFT_CHAIN_MANGLE, exprs.NFT_FAMILY_INET) + if tblmangle == nil { + t.Error("interception table mangle-inet not in the list") + } + if tableExists(t, nftest.Fw.Conn, tblmangle, exprs.NFT_FAMILY_INET) == false { + t.Error("table mangle-inet not in the list") + } + }) + t.Run("filter-inet", func(t *testing.T) { + tblfilter := nftest.Fw.GetTable(exprs.NFT_CHAIN_FILTER, exprs.NFT_FAMILY_INET) + if tblfilter == nil { + t.Error("interception table filter-inet not in the list") + } + if tableExists(t, nftest.Fw.Conn, tblfilter, exprs.NFT_FAMILY_INET) == false { + t.Error("table filter-inet not in the list") + } + }) +} diff --git a/daemon/firewall/nftables/testdata/test-sysfw-conf.json b/daemon/firewall/nftables/testdata/test-sysfw-conf.json new file mode 100644 index 0000000..089b204 --- /dev/null +++ b/daemon/firewall/nftables/testdata/test-sysfw-conf.json @@ -0,0 +1,159 @@ +{ + "Enabled": true, + "Version": 1, + "SystemRules": [ + { + "Chains": [ + { + "Name": "input", + "Table": "filter", + "Family": "inet", + "Priority": "", + "Type": "filter", + "Hook": "input", + "Policy": "accept", + "Rules": [ + { + "Enabled": true, + "Position": "0", + "Description": "Allow SSH server connections when input policy is DROP", + "Parameters": "", + "Expressions": [ + { + "Statement": { + "Op": "", + "Name": "tcp", + "Values": [ + { + "Key": "dport", + "Value": "22" + } + ] + } + } + ], + "Target": "accept", + "TargetParameters": "" + } + ] + }, + { + "Name": "output", + "Table": "mangle", + "Family": "inet", + "Priority": "", + "Type": "mangle", + "Hook": "output", + "Policy": "accept", + "Rules": [ + { + "Enabled": true, + "Position": "0", + "Description": "Allow ICMP", + "Expressions": [ + { + "Statement": { + "Op": "", + "Name": "icmp", + "Values": [ + { + "Key": "type", + "Value": "echo-request" + }, + { + "Key": "type", + "Value": "echo-reply" + } + ] + } + } + ], + "Target": "accept", + "TargetParameters": "" + }, + { + "Enabled": true, + "Position": "0", + "Description": "Allow ICMPv6", + "Expressions": [ + { + "Statement": { + "Op": "", + "Name": "icmpv6", + "Values": [ + { + "Key": "type", + "Value": "echo-request" + }, + { + "Key": "type", + "Value": "echo-reply" + } + ] + } + } + ], + "Target": "accept", + "TargetParameters": "" + }, + { + "Enabled": true, + "Position": "0", + "Description": "Exclude WireGuard VPN from being intercepted", + "Parameters": "", + "Expressions": [ + { + "Statement": { + "Op": "", + "Name": "udp", + "Values": [ + { + "Key": "dport", + "Value": "51820" + } + ] + } + } + ], + "Target": "accept", + "TargetParameters": "" + } + ] + }, + { + "Name": "forward", + "Table": "mangle", + "Family": "inet", + "Priority": "", + "Type": "mangle", + "Hook": "forward", + "Policy": "accept", + "Rules": [ + { + "UUID": "7d7394e1-100d-4b87-a90a-cd68c46edb0b", + "Enabled": true, + "Position": "0", + "Description": "Intercept forwarded connections (docker, etc)", + "Expressions": [ + { + "Statement": { + "Op": "", + "Name": "ct", + "Values": [ + { + "Key": "state", + "Value": "new" + } + ] + } + } + ], + "Target": "queue", + "TargetParameters": "num 0" + } + ] + } + ] + } + ] +} diff --git a/daemon/firewall/nftables/utils.go b/daemon/firewall/nftables/utils.go new file mode 100644 index 0000000..dbb3d19 --- /dev/null +++ b/daemon/firewall/nftables/utils.go @@ -0,0 +1,173 @@ +package nftables + +import ( + "strings" + + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/google/nftables" +) + +func GetFamilyCode(family string) nftables.TableFamily { + famCode := nftables.TableFamilyINet + switch family { + // [filter]: prerouting forward input output postrouting + // [nat]: prerouting, input output postrouting + // [route]: output + case exprs.NFT_FAMILY_IP6: + famCode = nftables.TableFamilyIPv6 + case exprs.NFT_FAMILY_IP: + famCode = nftables.TableFamilyIPv4 + case exprs.NFT_FAMILY_BRIDGE: + // [filter]: prerouting forward input output postrouting + famCode = nftables.TableFamilyBridge + case exprs.NFT_FAMILY_ARP: + // [filter]: input output + famCode = nftables.TableFamilyARP + case exprs.NFT_FAMILY_NETDEV: + // [filter]: egress, ingress + famCode = nftables.TableFamilyNetdev + } + + return famCode +} + +func GetHook(chain string) *nftables.ChainHook { + hook := nftables.ChainHookOutput + + // https://github.com/google/nftables/blob/master/chain.go#L33 + switch strings.ToLower(chain) { + case exprs.NFT_HOOK_INPUT: + hook = nftables.ChainHookInput + case exprs.NFT_HOOK_PREROUTING: + hook = nftables.ChainHookPrerouting + case exprs.NFT_HOOK_POSTROUTING: + hook = nftables.ChainHookPostrouting + case exprs.NFT_HOOK_FORWARD: + hook = nftables.ChainHookForward + case exprs.NFT_HOOK_INGRESS: + hook = nftables.ChainHookIngress + } + + return hook +} + +// GetChainPriority gets the corresponding priority for the given chain, based +// on the following configuration matrix: +// https://wiki.nftables.org/wiki-nftables/index.php/Netfilter_hooks#Priority_within_hook +// https://github.com/google/nftables/blob/master/chain.go#L48 +// man nft (table 6.) +func GetChainPriority(family, cType, hook string) (*nftables.ChainPriority, nftables.ChainType) { + // types: route, nat, filter + chainType := nftables.ChainTypeFilter + // priorities: raw, conntrack, mangle, natdest, filter, security + chainPrio := nftables.ChainPriorityFilter + + family = strings.ToLower(family) + cType = strings.ToLower(cType) + hook = strings.ToLower(hook) + + // constraints + // https://www.netfilter.org/projects/nftables/manpage.html#lbAQ + if (cType == exprs.NFT_CHAIN_NATDEST || cType == exprs.NFT_CHAIN_NATSOURCE) && hook == exprs.NFT_HOOK_FORWARD { + log.Warning("[nftables] invalid nat combination of tables and hooks. chain: %s, hook: %s", cType, hook) + return nil, chainType + } + if family == exprs.NFT_FAMILY_NETDEV && (cType != exprs.NFT_CHAIN_FILTER || hook != exprs.NFT_HOOK_INGRESS) { + log.Warning("[nftables] invalid netdev combination of tables and hooks. chain: %s, hook: %s", cType, hook) + return nil, chainType + } + if family == exprs.NFT_FAMILY_ARP && (cType != exprs.NFT_CHAIN_FILTER || (hook != exprs.NFT_HOOK_OUTPUT && hook != exprs.NFT_HOOK_INPUT)) { + log.Warning("[nftables] invalid arp combination of tables and hooks. chain: %s, hook: %s", cType, hook) + return nil, chainType + } + if family == exprs.NFT_FAMILY_BRIDGE && (cType != exprs.NFT_CHAIN_FILTER || (hook == exprs.NFT_HOOK_EGRESS || hook == exprs.NFT_HOOK_INGRESS)) { + log.Warning("[nftables] invalid bridge combination of tables and hooks. chain: %s, hook: %s", cType, hook) + return nil, chainType + } + + // Standard priority names, family and hook compatibility matrix + // https://www.netfilter.org/projects/nftables/manpage.html#lbAQ + + switch cType { + case exprs.NFT_CHAIN_FILTER: + if family == exprs.NFT_FAMILY_BRIDGE { + // bridge all filter -200 NF_BR_PRI_FILTER_BRIDGED + chainPrio = nftables.ChainPriorityConntrack + switch hook { + case exprs.NFT_HOOK_PREROUTING: // -300 + chainPrio = nftables.ChainPriorityRaw + case exprs.NFT_HOOK_OUTPUT: // -100 + chainPrio = nftables.ChainPriorityNATSource + case exprs.NFT_HOOK_POSTROUTING: // 300 + chainPrio = nftables.ChainPriorityConntrackHelper + } + } + case exprs.NFT_CHAIN_MANGLE: + // hooks: all + // XXX: check hook input? + chainPrio = nftables.ChainPriorityMangle + // https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains#Base_chain_types + // (...) equivalent semantics to the mangle table but only for the output hook (for other hooks use type filter instead). + + // Despite of what is said on the wiki, mangle chains must be of filter type, + // otherwise on some kernels (4.19.x) table MANGLE hook OUTPUT chain is not created + chainType = nftables.ChainTypeFilter + + case exprs.NFT_CHAIN_RAW: + // hook: all + chainPrio = nftables.ChainPriorityRaw + + case exprs.NFT_CHAIN_CONNTRACK: + chainPrio, chainType = GetConntrackPriority(hook) + + case exprs.NFT_CHAIN_NATDEST: + // hook: prerouting + chainPrio = nftables.ChainPriorityNATDest + switch hook { + case exprs.NFT_HOOK_OUTPUT: + chainPrio = nftables.ChainPriorityNATSource + } + chainType = nftables.ChainTypeNAT + + case exprs.NFT_CHAIN_NATSOURCE: + // hook: postrouting + chainPrio = nftables.ChainPriorityNATSource + chainType = nftables.ChainTypeNAT + + case exprs.NFT_CHAIN_SECURITY: + // hook: all + chainPrio = nftables.ChainPrioritySecurity + + case exprs.NFT_CHAIN_SELINUX: + // hook: all + if hook != exprs.NFT_HOOK_POSTROUTING { + chainPrio = nftables.ChainPrioritySELinuxLast + } else { + chainPrio = nftables.ChainPrioritySELinuxFirst + } + } + + return chainPrio, chainType +} + +// https://wiki.nftables.org/wiki-nftables/index.php/Netfilter_hooks#Priority_within_hook +func GetConntrackPriority(hook string) (*nftables.ChainPriority, nftables.ChainType) { + chainType := nftables.ChainTypeFilter + chainPrio := nftables.ChainPriorityConntrack + switch hook { + case exprs.NFT_HOOK_PREROUTING: + chainPrio = nftables.ChainPriorityConntrack + // ChainTypeNAT not allowed here + case exprs.NFT_HOOK_OUTPUT: + chainPrio = nftables.ChainPriorityNATSource // 100 - ChainPriorityConntrack + case exprs.NFT_HOOK_POSTROUTING: + chainPrio = nftables.ChainPriorityConntrackHelper + chainType = nftables.ChainTypeNAT + case exprs.NFT_HOOK_INPUT: + // can also be hook == NFT_HOOK_POSTROUTING + chainPrio = nftables.ChainPriorityConntrackConfirm + } + + return chainPrio, chainType +} diff --git a/daemon/firewall/nftables/utils_test.go b/daemon/firewall/nftables/utils_test.go new file mode 100644 index 0000000..999a44e --- /dev/null +++ b/daemon/firewall/nftables/utils_test.go @@ -0,0 +1,221 @@ +package nftables_test + +import ( + "testing" + + nftb "github.com/evilsocket/opensnitch/daemon/firewall/nftables" + "github.com/evilsocket/opensnitch/daemon/firewall/nftables/exprs" + "github.com/google/nftables" +) + +type chainPrioT struct { + test string + errorReason string + family string + chain string + hook string + checkEqual bool + chainPrio *nftables.ChainPriority + chainType nftables.ChainType +} + +// TestGetConntrackPriority test basic Conntrack chains priority configurations. +// https://wiki.nftables.org/wiki-nftables/index.php/Netfilter_hooks#Priority_within_hook +func TestGetConntrackPriority(t *testing.T) { + + t.Run("hook-prerouting", func(t *testing.T) { + cprio, ctype := nftb.GetConntrackPriority(exprs.NFT_HOOK_PREROUTING) + if cprio != nftables.ChainPriorityConntrack && ctype != nftables.ChainTypeFilter { + t.Errorf("invalid conntrack priority or type for hook PREROUTING: %+v, %+v", cprio, ctype) + } + }) + + t.Run("hook-output", func(t *testing.T) { + cprio, ctype := nftb.GetConntrackPriority(exprs.NFT_HOOK_OUTPUT) + if cprio != nftables.ChainPriorityNATSource && ctype != nftables.ChainTypeFilter { + t.Errorf("invalid conntrack priority or type for hook OUTPUT: %+v, %+v", cprio, ctype) + } + }) + + t.Run("hook-postrouting", func(t *testing.T) { + cprio, ctype := nftb.GetConntrackPriority(exprs.NFT_HOOK_POSTROUTING) + if cprio != nftables.ChainPriorityConntrackHelper && ctype != nftables.ChainTypeNAT { + t.Errorf("invalid conntrack priority or type for hook POSTROUTING: %+v, %+v", cprio, ctype) + } + }) + + t.Run("hook-input", func(t *testing.T) { + cprio, ctype := nftb.GetConntrackPriority(exprs.NFT_HOOK_INPUT) + if cprio != nftables.ChainPriorityConntrackConfirm && ctype != nftables.ChainTypeFilter { + t.Errorf("invalid conntrack priority or type for hook INPUT: %+v, %+v", cprio, ctype) + } + }) + +} + +// https://wiki.nftables.org/wiki-nftables/index.php/Netfilter_hooks#Priority_within_hook +// https://github.com/google/nftables/blob/master/chain.go#L48 +// man nft (table 6.) +func TestGetChainPriority(t *testing.T) { + matrixTests := []chainPrioT{ + // https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains#Base_chain_types + // (...) equivalent semantics to the mangle table but only for the output hook (for other hooks use type filter instead). + + // Despite of what is said on the wiki, mangle chains must be of filter type, + // otherwise on some kernels (4.19.x) table MANGLE hook OUTPUT chain is not created + { + "inet-mangle-output", + "invalid MANGLE chain priority or type: %+v-%+v <-> %v-%v", + exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_MANGLE, exprs.NFT_HOOK_OUTPUT, + true, + nftables.ChainPriorityMangle, nftables.ChainTypeFilter, + }, + { + "inet-natdest-output", + "invalid NATDest-output chain priority or type: %+v-%+v <-> %v-%v", + exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_NATDEST, exprs.NFT_HOOK_OUTPUT, + true, + nftables.ChainPriorityNATSource, nftables.ChainTypeNAT, + }, + { + "inet-natdest-prerouting", + "invalid NATDest-prerouting chain priority or type: %+v-%+v <-> %v-%v", + exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_NATDEST, exprs.NFT_HOOK_PREROUTING, + true, + nftables.ChainPriorityNATDest, nftables.ChainTypeNAT, + }, + { + "inet-natsource-postrouting", + "invalid NATSource-postrouting chain priority or type: %+v-%+v, %v-%v", + exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_NATSOURCE, exprs.NFT_HOOK_POSTROUTING, + true, + nftables.ChainPriorityNATSource, nftables.ChainTypeNAT, + }, + + // constraints + // https://www.netfilter.org/projects/nftables/manpage.html#lbAQ + { + "inet-natdest-forward", + "invalid natdest-forward chain: %+v-%+v <-> %v-%v", + exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_NATDEST, exprs.NFT_HOOK_FORWARD, + true, + nil, nftables.ChainTypeFilter, + }, + { + "inet-natsource-forward", + "invalid natsource-forward chain: %+v-%+v <-> %v-%v", + exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_NATSOURCE, exprs.NFT_HOOK_FORWARD, + true, + nil, nftables.ChainTypeFilter, + }, + { + "netdev-filter-ingress", + "invalid netdev chain prio or type: %+v-%+v <-> %v-%v", + exprs.NFT_FAMILY_NETDEV, exprs.NFT_CHAIN_FILTER, exprs.NFT_HOOK_INGRESS, + true, + nftables.ChainPriorityFilter, nftables.ChainTypeFilter, + }, + { + "arp-filter-input", + "invalid arp chain prio or type: %+v-%+v <-> %v-%v", + exprs.NFT_FAMILY_ARP, exprs.NFT_CHAIN_FILTER, exprs.NFT_HOOK_INPUT, + true, + nftables.ChainPriorityFilter, nftables.ChainTypeFilter, + }, + { + "bridge-filter-prerouting", + "invalid bridge-prerouting chain prio or type: %+v-%+v <-> %v-%v", + exprs.NFT_FAMILY_BRIDGE, exprs.NFT_CHAIN_FILTER, exprs.NFT_HOOK_PREROUTING, + true, + nftables.ChainPriorityRaw, nftables.ChainTypeFilter, + }, + { + "bridge-filter-output", + "invalid bridge-output chain prio or type: %+v-%+v <-> %v-%v", + exprs.NFT_FAMILY_BRIDGE, exprs.NFT_CHAIN_FILTER, exprs.NFT_HOOK_OUTPUT, + true, + nftables.ChainPriorityNATSource, nftables.ChainTypeFilter, + }, + { + "bridge-filter-postrouting", + "invalid bridge-postrouting chain prio or type: %+v-%+v <-> %v-%v", + exprs.NFT_FAMILY_BRIDGE, exprs.NFT_CHAIN_FILTER, exprs.NFT_HOOK_POSTROUTING, + true, + nftables.ChainPriorityConntrackHelper, nftables.ChainTypeFilter, + }, + } + + for _, testChainPrio := range matrixTests { + t.Run(testChainPrio.test, func(t *testing.T) { + chainPrio, chainType := nftb.GetChainPriority(testChainPrio.family, testChainPrio.chain, testChainPrio.hook) + + if testChainPrio.checkEqual { + if chainPrio != testChainPrio.chainPrio && chainType != testChainPrio.chainType { + t.Errorf(testChainPrio.errorReason, chainPrio, chainType, testChainPrio.chainPrio, testChainPrio.chainType) + } + } else { + if chainPrio == testChainPrio.chainPrio && chainType == testChainPrio.chainType { + t.Errorf(testChainPrio.errorReason, chainPrio, chainType, testChainPrio.chainPrio, testChainPrio.chainType) + } + } + }) + } + +} + +func TestInvalidChainPriority(t *testing.T) { + matrixTests := []chainPrioT{ + { + "inet-natdest-forward", + "natdest-forward chain should be invalid: %+v-%+v <-> %v-%v", + exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_NATDEST, exprs.NFT_HOOK_FORWARD, + true, + nil, nftables.ChainTypeFilter, + }, + { + "inet-natsource-forward", + "natsource-forward chain should be invalid: %+v-%+v <-> %v-%v", + exprs.NFT_FAMILY_INET, exprs.NFT_CHAIN_NATSOURCE, exprs.NFT_HOOK_FORWARD, + true, + nil, nftables.ChainTypeFilter, + }, + { + "netdev-natsource-forward", + "netdev chain should be invalid: %+v-%+v <-> %v-%v", + exprs.NFT_FAMILY_NETDEV, exprs.NFT_CHAIN_NATSOURCE, exprs.NFT_HOOK_FORWARD, + true, + nil, + nftables.ChainTypeFilter, + }, + { + "arp-natsource-forward", + "arp chain should be invalid: %+v-%+v <-> %v-%v", + exprs.NFT_FAMILY_ARP, exprs.NFT_CHAIN_NATSOURCE, exprs.NFT_HOOK_FORWARD, + true, + nil, nftables.ChainTypeFilter, + }, + { + "bridge-natsource-forward", + "bridge chain should be invalid: %+v-%+v <-> %v-%v", + exprs.NFT_FAMILY_ARP, exprs.NFT_CHAIN_NATSOURCE, exprs.NFT_HOOK_FORWARD, + true, + nil, nftables.ChainTypeFilter, + }, + } + + for _, testChainPrio := range matrixTests { + t.Run(testChainPrio.test, func(t *testing.T) { + chainPrio, chainType := nftb.GetChainPriority(testChainPrio.family, testChainPrio.chain, testChainPrio.hook) + + if testChainPrio.checkEqual { + if chainPrio != testChainPrio.chainPrio && chainType != testChainPrio.chainType { + } + } else { + if chainPrio == testChainPrio.chainPrio && chainType == testChainPrio.chainType { + t.Errorf(testChainPrio.errorReason, chainPrio, chainType, testChainPrio.chainPrio, testChainPrio.chainType) + } + } + }) + } + +} diff --git a/daemon/firewall/rules.go b/daemon/firewall/rules.go new file mode 100644 index 0000000..f764479 --- /dev/null +++ b/daemon/firewall/rules.go @@ -0,0 +1,160 @@ +package firewall + +import ( + "fmt" + + "github.com/evilsocket/opensnitch/daemon/firewall/common" + "github.com/evilsocket/opensnitch/daemon/firewall/iptables" + "github.com/evilsocket/opensnitch/daemon/firewall/nftables" + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/evilsocket/opensnitch/daemon/ui/protocol" +) + +// Firewall is the interface that all firewalls (iptables, nftables) must implement. +type Firewall interface { + Init(*int) + Stop() + Name() string + IsRunning() bool + SetQueueNum(num *int) + + SaveConfiguration(rawConfig string) error + + EnableInterception() + DisableInterception(bool) + QueueDNSResponses(bool, bool) (error, error) + QueueConnections(bool, bool) (error, error) + CleanRules(bool) + + AddSystemRules(bool, bool) + DeleteSystemRules(bool, bool, bool) + + Serialize() (*protocol.SysFirewall, error) + Deserialize(sysfw *protocol.SysFirewall) ([]byte, error) + + ErrorsChan() <-chan string + ErrChanEmpty() bool +} + +var ( + fw Firewall + queueNum = 0 +) + +// Init initializes the firewall and loads firewall rules. +// We'll try to use the firewall configured in the configuration (iptables/nftables). +// If iptables is not installed, we can add nftables rules directly to the kernel, +// without relying on any binaries. +func Init(fwType string, qNum *int) (err error) { + if fwType == iptables.Name { + fw, err = iptables.Fw() + if err != nil { + log.Warning("iptables not available: %s", err) + } + } + + if fwType == nftables.Name || err != nil { + fw, err = nftables.Fw() + if err != nil { + log.Warning("nftables not available: %s", err) + } + } + + if err != nil { + return fmt.Errorf("firewall error: %s, not iptables nor nftables are available or are usable. Please, report it on github", err) + } + + if fw == nil { + return fmt.Errorf("Firewall not initialized") + } + fw.Stop() + fw.Init(qNum) + queueNum = *qNum + + log.Info("Using %s firewall", fw.Name()) + + return +} + +// IsRunning returns if the firewall is running or not. +func IsRunning() bool { + return fw != nil && fw.IsRunning() +} + +// ErrorsChan returns the channel where the errors are sent to. +func ErrorsChan() <-chan string { + return fw.ErrorsChan() +} + +// ErrChanEmpty checks if the errors channel is empty. +func ErrChanEmpty() bool { + return fw.ErrChanEmpty() +} + +// CleanRules deletes the rules we added. +func CleanRules(logErrors bool) { + if fw == nil { + return + } + fw.CleanRules(logErrors) +} + +// ChangeFw stops current firewall and initializes a new one. +func ChangeFw(fwtype string) (err error) { + Stop() + err = Init(fwtype, &queueNum) + return +} + +// Reload deletes existing firewall rules and readds them. +func Reload() { + fw.Stop() + fw.Init(&queueNum) +} + +// ReloadSystemRules deletes existing rules, and add them again +func ReloadSystemRules() { + fw.DeleteSystemRules(!common.ForcedDelRules, common.RestoreChains, true) + fw.AddSystemRules(common.ReloadRules, common.BackupChains) +} + +// EnableInterception removes the rules to intercept outbound connections. +func EnableInterception() error { + if fw == nil { + return fmt.Errorf("firewall not initialized when trying to enable interception, report please") + } + fw.EnableInterception() + return nil +} + +// DisableInterception removes the rules to intercept outbound connections. +func DisableInterception() error { + if fw == nil { + return fmt.Errorf("firewall not initialized when trying to disable interception, report please") + } + fw.DisableInterception(true) + return nil +} + +// Stop deletes the firewall rules, allowing network traffic. +func Stop() { + if fw == nil { + return + } + fw.Stop() +} + +// SaveConfiguration saves configuration string to disk +func SaveConfiguration(rawConfig []byte) error { + return fw.SaveConfiguration(string(rawConfig)) +} + +// Serialize transforms firewall json configuration to protobuf +func Serialize() (*protocol.SysFirewall, error) { + return fw.Serialize() +} + +// Deserialize transforms firewall json configuration to protobuf +func Deserialize(sysfw *protocol.SysFirewall) ([]byte, error) { + return fw.Deserialize(sysfw) +} diff --git a/daemon/go.mod b/daemon/go.mod new file mode 100644 index 0000000..b6e4a7f --- /dev/null +++ b/daemon/go.mod @@ -0,0 +1,33 @@ +module github.com/evilsocket/opensnitch/daemon + +go 1.17 + +require ( + github.com/fsnotify/fsnotify v1.4.7 + github.com/golang/protobuf v1.5.0 + github.com/google/gopacket v1.1.14 + github.com/google/nftables v0.1.0 + github.com/google/uuid v1.3.0 + github.com/iovisor/gobpf v0.2.0 + github.com/varlink/go v0.4.0 + github.com/vishvananda/netlink v0.0.0-20210811191823-e1a867c6b452 + github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae + golang.org/x/net v0.0.0-20211209124913-491a49abca63 + golang.org/x/sys v0.0.0-20211205182925-97ca703d548d + google.golang.org/grpc v1.32.0 +) + +require ( + github.com/BurntSushi/toml v0.4.1 // indirect + github.com/google/go-cmp v0.5.6 // indirect + github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 // indirect + github.com/mdlayher/netlink v1.4.2 // indirect + github.com/mdlayher/socket v0.0.0-20211102153432-57e3fa563ecb // indirect + golang.org/x/mod v0.5.1 // indirect + golang.org/x/text v0.3.7 // indirect + golang.org/x/tools v0.1.8 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 // indirect + google.golang.org/protobuf v1.26.0 // indirect + honnef.co/go/tools v0.2.2 // indirect +) diff --git a/daemon/go.sum b/daemon/go.sum new file mode 100644 index 0000000..9b14c3b --- /dev/null +++ b/daemon/go.sum @@ -0,0 +1,197 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw= +github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= +github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gopacket v1.1.14 h1:1+TEhSu8Mh154ZBVjyd1Nt2Bb7cnyOeE3GQyb1WGLqI= +github.com/google/gopacket v1.1.14/go.mod h1:UCLx9mCmAwsVbn6qQl1WIEt2SO7Nd2fD0th1TBAsqBw= +github.com/google/nftables v0.1.0 h1:T6lS4qudrMufcNIZ8wSRrL+iuwhsKxpN+zFLxhUWOqk= +github.com/google/nftables v0.1.0/go.mod h1:b97ulCCFipUC+kSin+zygkvUVpx0vyIAwxXFdY3PlNc= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/iovisor/gobpf v0.2.0 h1:34xkQxft+35GagXBk3n23eqhm0v7q0ejeVirb8sqEOQ= +github.com/iovisor/gobpf v0.2.0/go.mod h1:WSY9Jj5RhdgC3ci1QaacvbFdQ8cbrEjrpiZbLHLt2s4= +github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 h1:uhL5Gw7BINiiPAo24A2sxkcDI0Jt/sqp1v5xQCniEFA= +github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= +github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= +github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= +github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= +github.com/jsimonetti/rtnetlink v0.0.0-20201216134343-bde56ed16391/go.mod h1:cR77jAZG3Y3bsb8hF6fHJbFoyFukLFOkQ98S0pQz3xw= +github.com/jsimonetti/rtnetlink v0.0.0-20201220180245-69540ac93943/go.mod h1:z4c53zj6Eex712ROyh8WI0ihysb5j2ROyV42iNogmAs= +github.com/jsimonetti/rtnetlink v0.0.0-20210122163228-8d122574c736/go.mod h1:ZXpIyOK59ZnN7J0BV99cZUPmsqDRZ3eq5X+st7u/oSA= +github.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b/go.mod h1:8w9Rh8m+aHZIG69YPGGem1i5VzoyRC8nw2kA8B+ik5U= +github.com/jsimonetti/rtnetlink v0.0.0-20210525051524-4cc836578190/go.mod h1:NmKSdU4VGSiv1bMsdqNALI4RSvvjtz65tTMCnD05qLo= +github.com/jsimonetti/rtnetlink v0.0.0-20211022192332-93da33804786 h1:N527AHMa793TP5z5GNAn/VLPzlc0ewzWdeP/25gDfgQ= +github.com/jsimonetti/rtnetlink v0.0.0-20211022192332-93da33804786/go.mod h1:v4hqbTdfQngbVSZJVWUhGE/lbTFf9jb+ygmNUDQMuOs= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo= +github.com/mdlayher/ethtool v0.0.0-20211028163843-288d040e9d60 h1:tHdB+hQRHU10CfcK0furo6rSNgZ38JT8uPh70c/pFD8= +github.com/mdlayher/ethtool v0.0.0-20211028163843-288d040e9d60/go.mod h1:aYbhishWc4Ai3I2U4Gaa2n3kHWSwzme6EsG/46HRQbE= +github.com/mdlayher/genetlink v1.0.0 h1:OoHN1OdyEIkScEmRgxLEe2M9U8ClMytqA5niynLtfj0= +github.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc= +github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= +github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= +github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= +github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= +github.com/mdlayher/netlink v1.2.0/go.mod h1:kwVW1io0AZy9A1E2YYgaD4Cj+C+GPkU6klXCMzIJ9p8= +github.com/mdlayher/netlink v1.2.1/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU= +github.com/mdlayher/netlink v1.2.2-0.20210123213345-5cc92139ae3e/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU= +github.com/mdlayher/netlink v1.3.0/go.mod h1:xK/BssKuwcRXHrtN04UBkwQ6dY9VviGGuriDdoPSWys= +github.com/mdlayher/netlink v1.4.0/go.mod h1:dRJi5IABcZpBD2A3D0Mv/AiX8I9uDEu5oGkAVrekmf8= +github.com/mdlayher/netlink v1.4.1/go.mod h1:e4/KuJ+s8UhfUpO9z00/fDZZmhSrs+oxyqAS9cNgn6Q= +github.com/mdlayher/netlink v1.4.2 h1:3sbnJWe/LETovA7yRZIX3f9McVOWV3OySH6iIBxiFfI= +github.com/mdlayher/netlink v1.4.2/go.mod h1:13VaingaArGUTUxFLf/iEovKxXji32JAtF858jZYEug= +github.com/mdlayher/socket v0.0.0-20210307095302-262dc9984e00/go.mod h1:GAFlyu4/XV68LkQKYzKhIo/WW7j3Zi0YRAz/BOoanUc= +github.com/mdlayher/socket v0.0.0-20211007213009-516dcbdf0267/go.mod h1:nFZ1EtZYK8Gi/k6QNu7z7CgO20i/4ExeQswwWuPmG/g= +github.com/mdlayher/socket v0.0.0-20211102153432-57e3fa563ecb h1:2dC7L10LmTqlyMVzFJ00qM25lqESg9Z4u3GuEXN5iHY= +github.com/mdlayher/socket v0.0.0-20211102153432-57e3fa563ecb/go.mod h1:nFZ1EtZYK8Gi/k6QNu7z7CgO20i/4ExeQswwWuPmG/g= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/varlink/go v0.4.0 h1:+/BQoUO9eJK/+MTSHwFcJch7TMsb6N6Dqp6g0qaXXRo= +github.com/varlink/go v0.4.0/go.mod h1:DKg9Y2ctoNkesREGAEak58l+jOC6JU2aqZvUYs5DynU= +github.com/vishvananda/netlink v0.0.0-20210811191823-e1a867c6b452 h1:xe1bLd/sNkKVWdZuAb2+4JeMQMYyQ7Av38iRrE1lhm8= +github.com/vishvananda/netlink v0.0.0-20210811191823-e1a867c6b452/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= +github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= +github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae h1:4hwBBUfQCFe3Cym0ZtKyq7L16eZUtYKs+BaHDN6mAns= +github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211201190559-0a0e4e1bb54c/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY= +golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201118182958-a01c418693c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201218084310-7d0127a74742/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210110051926-789bb1bd4061/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210123111255-9b0068b26619/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210216163648-f7da38b97c65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211205182925-97ca703d548d h1:FjkYO/PPp4Wi0EAUOVLxePm7qVW4r4ctbWpURyuOD0E= +golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w= +golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= +google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.2.1/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY= +honnef.co/go/tools v0.2.2 h1:MNh1AVMyVX23VUHE2O27jm6lNj3vjO5DexS4A1xvnzk= +honnef.co/go/tools v0.2.2/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY= diff --git a/daemon/log/formats/csv.go b/daemon/log/formats/csv.go new file mode 100644 index 0000000..3e3d832 --- /dev/null +++ b/daemon/log/formats/csv.go @@ -0,0 +1,50 @@ +package formats + +import ( + "fmt" + + "github.com/evilsocket/opensnitch/daemon/ui/protocol" +) + +// CSV name of the output format, used in json configs +const CSV = "csv" + +// Csv object +type Csv struct { +} + +// NewCSV returns a new CSV transformer object. +func NewCSV() *Csv { + return &Csv{} +} + +// Transform takes input arguments and formats them to CSV. +func (c *Csv) Transform(args ...interface{}) (out string) { + p := args[0] + values := p.([]interface{}) + for _, val := range values { + switch val.(type) { + case *protocol.Connection: + con := val.(*protocol.Connection) + out = fmt.Sprint(out, + con.SrcIp, ",", + con.SrcPort, ",", + con.DstIp, ",", + con.DstHost, ",", + con.DstPort, ",", + con.Protocol, ",", + con.ProcessId, ",", + con.UserId, ",", + //con.ProcessComm, ",", + con.ProcessPath, ",", + con.ProcessArgs, ",", + con.ProcessCwd, ",", + ) + default: + out = fmt.Sprint(out, val, ",") + } + } + out = out[:len(out)-1] + + return +} diff --git a/daemon/log/formats/formats.go b/daemon/log/formats/formats.go new file mode 100644 index 0000000..efb3090 --- /dev/null +++ b/daemon/log/formats/formats.go @@ -0,0 +1,9 @@ +package formats + +// LoggerFormat is the common interface that every format must meet. +// Transform expects an arbitrary number of arguments and types, and +// it must transform them to a string. +// Arguments can be of type Connection, string, int, etc. +type LoggerFormat interface { + Transform(...interface{}) string +} diff --git a/daemon/log/formats/json.go b/daemon/log/formats/json.go new file mode 100644 index 0000000..438c032 --- /dev/null +++ b/daemon/log/formats/json.go @@ -0,0 +1,69 @@ +package formats + +import ( + "encoding/json" + "fmt" + + "github.com/evilsocket/opensnitch/daemon/ui/protocol" +) + +// JSON name of the output format, used in our json config +const JSON = "json" + +// events types +const ( + EvConnection = iota + EvExec +) + +// JSONEventFormat object to be sent to the remote service. +// TODO: Expand as needed: ebpf events, etc. +type JSONEventFormat struct { + Event interface{} `json:"Event"` + Rule string `json:"Rule"` + Action string `json:"Action"` + Type uint8 `json:"Type"` +} + +// NewJSON returns a new Json format, to send events as json. +// The json is the protobuffer in json format. +func NewJSON() *JSONEventFormat { + return &JSONEventFormat{} +} + +// Transform takes input arguments and formats them to JSON format. +func (j *JSONEventFormat) Transform(args ...interface{}) (out string) { + p := args[0] + jObj := &JSONEventFormat{} + + values := p.([]interface{}) + for n, val := range values { + switch val.(type) { + // TODO: + // case *protocol.Rule: + // case *protocol.Process: + // case *protocol.Alerts: + case *protocol.Connection: + // XXX: All fields of the Connection object are sent, is this what we want? + // or should we send an anonymous json? + jObj.Event = val.(*protocol.Connection) + jObj.Type = EvConnection + + case string: + // action + // rule name + if n == 1 { + jObj.Action = val.(string) + } else if n == 2 { + jObj.Rule = val.(string) + } + } + } + + rawCfg, err := json.Marshal(&jObj) + if err != nil { + return + } + out = fmt.Sprint(string(rawCfg), "\n\n") + return +} diff --git a/daemon/log/formats/rfc3164.go b/daemon/log/formats/rfc3164.go new file mode 100644 index 0000000..7138bac --- /dev/null +++ b/daemon/log/formats/rfc3164.go @@ -0,0 +1,68 @@ +package formats + +import ( + "fmt" + "log/syslog" + "os" + "time" + + "github.com/evilsocket/opensnitch/daemon/ui/protocol" +) + +// RFC3164 name of the output format, used in our json config +const RFC3164 = "rfc3164" + +// Rfc3164 object +type Rfc3164 struct { + seq int +} + +// NewRfc3164 returns a new Rfc3164 object, that transforms a message to +// RFC3164 format. +func NewRfc3164() *Rfc3164 { + return &Rfc3164{} +} + +// Transform takes input arguments and formats them to RFC3164 format. +func (r *Rfc3164) Transform(args ...interface{}) (out string) { + hostname := "" + tag := "" + arg1 := args[0] + // we can do this better. Think. + if len(args) > 1 { + hostname = args[1].(string) + tag = args[2].(string) + } + values := arg1.([]interface{}) + for n, val := range values { + switch val.(type) { + case *protocol.Connection: + con := val.(*protocol.Connection) + out = fmt.Sprint(out, + " SRC=\"", con.SrcIp, "\"", + " SPT=\"", con.SrcPort, "\"", + " DST=\"", con.DstIp, "\"", + " DSTHOST=\"", con.DstHost, "\"", + " DPT=\"", con.DstPort, "\"", + " PROTO=\"", con.Protocol, "\"", + " PID=\"", con.ProcessId, "\"", + " UID=\"", con.UserId, "\"", + //" COMM=", con.ProcessComm, "\"", + " PATH=\"", con.ProcessPath, "\"", + " CMDLINE=\"", con.ProcessArgs, "\"", + " CWD=\"", con.ProcessCwd, "\"", + ) + default: + out = fmt.Sprint(out, " ARG", n, "=\"", val, "\"") + } + } + out = fmt.Sprintf("<%d>%s %s %s[%d]: [%s]\n", + syslog.LOG_NOTICE|syslog.LOG_DAEMON, + time.Now().Format(time.RFC3339), + hostname, + tag, + os.Getpid(), + out[1:]) + + return +} diff --git a/daemon/log/formats/rfc5424.go b/daemon/log/formats/rfc5424.go new file mode 100644 index 0000000..40e8c91 --- /dev/null +++ b/daemon/log/formats/rfc5424.go @@ -0,0 +1,69 @@ +package formats + +import ( + "fmt" + "log/syslog" + "os" + "time" + + "github.com/evilsocket/opensnitch/daemon/ui/protocol" +) + +// RFC5424 name of the output format, used in our json config +const RFC5424 = "rfc5424" + +// Rfc5424 object +type Rfc5424 struct { + seq int +} + +// NewRfc5424 returns a new Rfc5424 object, that transforms a message to +// RFC5424 format (sort of). +func NewRfc5424() *Rfc5424 { + return &Rfc5424{} +} + +// Transform takes input arguments and formats them to RFC5424 format. +func (r *Rfc5424) Transform(args ...interface{}) (out string) { + hostname := "" + tag := "" + arg1 := args[0] + if len(args) > 1 { + arg2 := args[1] + arg3 := args[2] + hostname = arg2.(string) + tag = arg3.(string) + } + values := arg1.([]interface{}) + for n, val := range values { + switch val.(type) { + case *protocol.Connection: + con := val.(*protocol.Connection) + out = fmt.Sprint(out, + " SRC=\"", con.SrcIp, "\"", + " SPT=\"", con.SrcPort, "\"", + " DST=\"", con.DstIp, "\"", + " DSTHOST=\"", con.DstHost, "\"", + " DPT=\"", con.DstPort, "\"", + " PROTO=\"", con.Protocol, "\"", + " PID=\"", con.ProcessId, "\"", + " UID=\"", con.UserId, "\"", + //" COMM=", con.ProcessComm, "\"", + " PATH=\"", con.ProcessPath, "\"", + " CMDLINE=\"", con.ProcessArgs, "\"", + " CWD=\"", con.ProcessCwd, "\"", + ) + default: + out = fmt.Sprint(out, " ARG", n, "=\"", val, "\"") + } + } + out = fmt.Sprintf("<%d>1 %s %s %s %d TCPOUT - [%s]\n", + syslog.LOG_NOTICE|syslog.LOG_DAEMON, + time.Now().Format(time.RFC3339), + hostname, + tag, + os.Getpid(), + out[1:]) + + return +} diff --git a/daemon/log/log.go b/daemon/log/log.go new file mode 100644 index 0000000..0602124 --- /dev/null +++ b/daemon/log/log.go @@ -0,0 +1,253 @@ +package log + +import ( + "fmt" + "os" + "strings" + "sync" + "time" +) + +type Handler func(format string, args ...interface{}) + +// https://misc.flogisoft.com/bash/tip_colors_and_formatting +const ( + BOLD = "\033[1m" + DIM = "\033[2m" + + RED = "\033[31m" + GREEN = "\033[32m" + BLUE = "\033[34m" + YELLOW = "\033[33m" + + FG_BLACK = "\033[30m" + FG_WHITE = "\033[97m" + + BG_DGRAY = "\033[100m" + BG_RED = "\033[41m" + BG_GREEN = "\033[42m" + BG_YELLOW = "\033[43m" + BG_LBLUE = "\033[104m" + + RESET = "\033[0m" +) + +// log level constants +const ( + DEBUG = iota + INFO + IMPORTANT + WARNING + ERROR + FATAL +) + +// +var ( + WithColors = true + Output = os.Stdout + StdoutFile = "/dev/stdout" + DateFormat = "2006-01-02 15:04:05" + MinLevel = INFO + LogUTC = true + LogMicro = false + + mutex = &sync.RWMutex{} + labels = map[int]string{ + DEBUG: "DBG", + INFO: "INF", + IMPORTANT: "IMP", + WARNING: "WAR", + ERROR: "ERR", + FATAL: "!!!", + } + colors = map[int]string{ + DEBUG: DIM + FG_BLACK + BG_DGRAY, + INFO: FG_WHITE + BG_GREEN, + IMPORTANT: FG_WHITE + BG_LBLUE, + WARNING: FG_WHITE + BG_YELLOW, + ERROR: FG_WHITE + BG_RED, + FATAL: FG_WHITE + BG_RED + BOLD, + } +) + +// Wrap wraps a text with effects +func Wrap(s, effect string) string { + if WithColors == true { + s = effect + s + RESET + } + return s +} + +// Dim dims a text +func Dim(s string) string { + return Wrap(s, DIM) +} + +// Bold bolds a text +func Bold(s string) string { + return Wrap(s, BOLD) +} + +// Red reds the text +func Red(s string) string { + return Wrap(s, RED) +} + +// Green greens the text +func Green(s string) string { + return Wrap(s, GREEN) +} + +// Blue blues the text +func Blue(s string) string { + return Wrap(s, BLUE) +} + +// Yellow yellows the text +func Yellow(s string) string { + return Wrap(s, YELLOW) +} + +// Raw prints out a text without colors +func Raw(format string, args ...interface{}) { + mutex.RLock() + defer mutex.RUnlock() + fmt.Fprintf(Output, format, args...) +} + +// SetLogLevel sets the log level +func SetLogLevel(newLevel int) { + mutex.Lock() + defer mutex.Unlock() + MinLevel = newLevel +} + +// GetLogLevel returns the current log level configured. +func GetLogLevel() int { + mutex.RLock() + defer mutex.RUnlock() + + return MinLevel +} + +// SetLogUTC configures UTC timestamps +func SetLogUTC(newLogUTC bool) { + mutex.Lock() + defer mutex.Unlock() + LogUTC = newLogUTC +} + +// GetLogUTC returns the current config. +func GetLogUTC() bool { + mutex.RLock() + defer mutex.RUnlock() + + return LogUTC +} + +// SetLogMicro configures microsecond timestamps +func SetLogMicro(newLogMicro bool) { + mutex.Lock() + defer mutex.Unlock() + LogMicro = newLogMicro +} + +// GetLogMicro returns the current config. +func GetLogMicro() bool { + mutex.Lock() + defer mutex.Unlock() + + return LogMicro +} + +// Log prints out a text with the given color and format +func Log(level int, format string, args ...interface{}) { + mutex.Lock() + defer mutex.Unlock() + if level >= MinLevel { + label := labels[level] + color := colors[level] + + datefmt := DateFormat + + if LogMicro == true { + datefmt = DateFormat + ".000000" + } + when := time.Now().UTC().Format(datefmt) + if LogUTC == false { + when = time.Now().Local().Format(datefmt) + } + + what := fmt.Sprintf(format, args...) + if strings.HasSuffix(what, "\n") == false { + what += "\n" + } + + l := Dim("[%s]") + r := Wrap(" %s ", color) + " %s" + + fmt.Fprintf(Output, l+" "+r, when, label, what) + } +} + +func setDefaultLogOutput() { + mutex.Lock() + Output = os.Stdout + mutex.Unlock() +} + +// OpenFile opens a file to print out the logs +func OpenFile(logFile string) (err error) { + if logFile == StdoutFile { + setDefaultLogOutput() + return + } + + if Output, err = os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644); err != nil { + Error("Error opening log: %s %s", logFile, err) + //fallback to stdout + setDefaultLogOutput() + } + Important("Start writing logs to %s", logFile) + + return err +} + +// Close closes the current output file descriptor +func Close() { + if Output != os.Stdout { + Output.Close() + } +} + +// Debug is the log level for debugging purposes +func Debug(format string, args ...interface{}) { + Log(DEBUG, format, args...) +} + +// Info is the log level for informative messages +func Info(format string, args ...interface{}) { + Log(INFO, format, args...) +} + +// Important is the log level for things that must pay attention +func Important(format string, args ...interface{}) { + Log(IMPORTANT, format, args...) +} + +// Warning is the log level for non-critical errors +func Warning(format string, args ...interface{}) { + Log(WARNING, format, args...) +} + +// Error is the log level for errors that should be corrected +func Error(format string, args ...interface{}) { + Log(ERROR, format, args...) +} + +// Fatal is the log level for errors that must be corrected before continue +func Fatal(format string, args ...interface{}) { + Log(FATAL, format, args...) + os.Exit(1) +} diff --git a/daemon/log/loggers/logger.go b/daemon/log/loggers/logger.go new file mode 100644 index 0000000..4fc81e1 --- /dev/null +++ b/daemon/log/loggers/logger.go @@ -0,0 +1,106 @@ +package loggers + +import "fmt" + +const logTag = "opensnitch" + +// Logger is the common interface that every logger must met. +// Serves as a generic holder of different types of loggers. +type Logger interface { + Transform(...interface{}) string + Write(string) +} + +// LoggerConfig holds the configuration of a logger +type LoggerConfig struct { + // Name of the logger: syslog, elastic, ... + Name string + // Format: rfc5424, csv, json, ... + Format string + // Protocol: udp, tcp + Protocol string + // Server: 127.0.0.1:514 + Server string + // WriteTimeout: + WriteTimeout string + // Tag: opensnitchd, mytag, ... + Tag string + // Workers: number of workers + Workers int +} + +// LoggerManager represents the LoggerManager. +type LoggerManager struct { + loggers map[string]Logger + msgs chan []interface{} + count int +} + +// NewLoggerManager instantiates all the configured loggers. +func NewLoggerManager() *LoggerManager { + lm := &LoggerManager{ + loggers: make(map[string]Logger), + } + + return lm +} + +// Load loggers configuration and initialize them. +func (l *LoggerManager) Load(configs []LoggerConfig, workers int) { + for _, cfg := range configs { + switch cfg.Name { + case LOGGER_REMOTE: + if lgr, err := NewRemote(&cfg); err == nil { + l.count++ + l.loggers[fmt.Sprint(lgr.Name, lgr.cfg.Server, lgr.cfg.Protocol)] = lgr + workers += cfg.Workers + } + case LOGGER_REMOTE_SYSLOG: + if lgr, err := NewRemoteSyslog(&cfg); err == nil { + l.count++ + l.loggers[fmt.Sprint(lgr.Name, lgr.cfg.Server, lgr.cfg.Protocol)] = lgr + workers += cfg.Workers + } + case LOGGER_SYSLOG: + if lgr, err := NewSyslog(&cfg); err == nil { + l.count++ + l.loggers[lgr.Name] = lgr + workers += cfg.Workers + } + } + } + + if workers == 0 { + workers = 4 + } + + l.msgs = make(chan []interface{}, workers) + for i := 0; i < workers; i++ { + go newWorker(i, l) + } + +} + +func (l *LoggerManager) write(args ...interface{}) { + for _, logger := range l.loggers { + logger.Write(logger.Transform(args...)) + } +} + +func newWorker(id int, l *LoggerManager) { + for { + for msg := range l.msgs { + l.write(msg) + } + } +} + +// Log sends data to the loggers. +func (l *LoggerManager) Log(args ...interface{}) { + if l.count > 0 { + go func(args ...interface{}) { + argv := args + l.msgs <- argv + }(args...) + } +} diff --git a/daemon/log/loggers/remote.go b/daemon/log/loggers/remote.go new file mode 100644 index 0000000..7936c2f --- /dev/null +++ b/daemon/log/loggers/remote.go @@ -0,0 +1,184 @@ +package loggers + +import ( + "fmt" + "log/syslog" + "net" + "os" + "strings" + "sync" + "sync/atomic" + "time" + + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/evilsocket/opensnitch/daemon/log/formats" +) + +const ( + LOGGER_REMOTE = "remote" +) + +// Remote defines the logger that writes events to a generic remote server. +// It can write to the local or a remote daemon, UDP or TCP. +// It supports writing events in RFC5424, RFC3164, CSV and JSON formats. +type Remote struct { + Name string + Tag string + Hostname string + + Writer *syslog.Writer + logFormat formats.LoggerFormat + cfg *LoggerConfig + netConn net.Conn + Timeout time.Duration + errors uint32 + maxErrors uint32 + status uint32 + + mu *sync.RWMutex +} + +// NewRemote returns a new object that manipulates and prints outbound connections +// to a remote syslog server, with the given format (RFC5424 by default) +func NewRemote(cfg *LoggerConfig) (*Remote, error) { + var err error + log.Info("NewRemote logger: %v", cfg) + + sys := &Remote{ + mu: &sync.RWMutex{}, + } + sys.Name = LOGGER_REMOTE + sys.cfg = cfg + + // list of allowed formats for this logger + sys.logFormat = formats.NewRfc5424() + if cfg.Format == formats.RFC3164 { + sys.logFormat = formats.NewRfc3164() + } else if cfg.Format == formats.JSON { + sys.logFormat = formats.NewJSON() + } else if cfg.Format == formats.CSV { + sys.logFormat = formats.NewCSV() + } + + sys.Tag = logTag + if cfg.Tag != "" { + sys.Tag = cfg.Tag + } + sys.Hostname, err = os.Hostname() + if err != nil { + sys.Hostname = "localhost" + } + if cfg.WriteTimeout == "" { + cfg.WriteTimeout = writeTimeout + } + sys.Timeout = (time.Second * 15) + + if err = sys.Open(); err != nil { + log.Error("Error loading logger: %s", err) + return nil, err + } + log.Info("[%s] initialized: %v", sys.Name, cfg) + + return sys, err +} + +// Open opens a new connection with a server or with the daemon. +func (s *Remote) Open() (err error) { + atomic.StoreUint32(&s.errors, 0) + if s.cfg.Server == "" { + return fmt.Errorf("[%s] Server address must not be empty", s.Name) + } + s.mu.Lock() + s.netConn, err = s.Dial(s.cfg.Protocol, s.cfg.Server, s.Timeout*5) + s.mu.Unlock() + + if err == nil { + atomic.StoreUint32(&s.status, CONNECTED) + } + return err +} + +// Dial opens a new connection with a remote server. +func (s *Remote) Dial(proto, addr string, connTimeout time.Duration) (netConn net.Conn, err error) { + switch proto { + case "udp", "tcp": + netConn, err = net.DialTimeout(proto, addr, connTimeout) + if err != nil { + return nil, err + } + default: + return nil, fmt.Errorf("[%s] Network protocol %s not supported", s.Name, proto) + } + + return netConn, nil +} + +// Close closes the writer object +func (s *Remote) Close() (err error) { + s.mu.RLock() + if s.netConn != nil { + err = s.netConn.Close() + //s.netConn.conn = nil + } + s.mu.RUnlock() + atomic.StoreUint32(&s.status, DISCONNECTED) + return +} + +// ReOpen tries to reestablish the connection with the writer +func (s *Remote) ReOpen() { + if atomic.LoadUint32(&s.status) == CONNECTING { + return + } + atomic.StoreUint32(&s.status, CONNECTING) + if err := s.Close(); err != nil { + log.Debug("[%s] error closing Close(): %s", s.Name, err) + } + + if err := s.Open(); err != nil { + log.Debug("[%s] ReOpen() error: %s", s.Name, err) + } else { + log.Debug("[%s] ReOpen() ok", s.Name) + } +} + +// Transform transforms data for proper ingestion. +func (s *Remote) Transform(args ...interface{}) (out string) { + if s.logFormat != nil { + args = append(args, s.Hostname) + args = append(args, s.Tag) + out = s.logFormat.Transform(args...) + } + return +} + +func (s *Remote) Write(msg string) { + deadline := time.Now().Add(s.Timeout) + + // BUG: it's fairly common to have write timeouts via udp/tcp. + // Reopening the connection with the server helps to resume sending events to the server, + // and have a continuous stream of events. Otherwise it'd stop working. + // I haven't figured out yet why these write errors ocurr. + s.mu.Lock() + s.netConn.SetWriteDeadline(deadline) + _, err := s.netConn.Write([]byte(msg)) + s.mu.Unlock() + if err == nil { + return + } + + log.Debug("[%s] %s write error: %v", s.Name, s.cfg.Protocol, err.(net.Error)) + atomic.AddUint32(&s.errors, 1) + if atomic.LoadUint32(&s.errors) > maxAllowedErrors { + s.ReOpen() + return + } +} + +func (s *Remote) formatLine(msg string) string { + nl := "" + if !strings.HasSuffix(msg, "\n") { + nl = "\n" + } + return fmt.Sprintf("%s%s", msg, nl) +} diff --git a/daemon/log/loggers/remote_syslog.go b/daemon/log/loggers/remote_syslog.go new file mode 100644 index 0000000..e67b86e --- /dev/null +++ b/daemon/log/loggers/remote_syslog.go @@ -0,0 +1,180 @@ +package loggers + +import ( + "fmt" + "net" + "os" + "sync" + "sync/atomic" + "time" + + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/evilsocket/opensnitch/daemon/log/formats" +) + +const ( + LOGGER_REMOTE_SYSLOG = "remote_syslog" + writeTimeout = "1s" + // restart syslog connection after these amount of errors + maxAllowedErrors = 10 +) + +// connection status +const ( + DISCONNECTED = iota + CONNECTED + CONNECTING +) + +// RemoteSyslog defines the logger that writes traces to the syslog. +// It can write to the local or a remote daemon. +type RemoteSyslog struct { + Syslog + + Hostname string + netConn net.Conn + Timeout time.Duration + errors uint32 + status uint32 + + mu *sync.RWMutex +} + +// NewRemoteSyslog returns a new object that manipulates and prints outbound connections +// to a remote syslog server, with the given format (RFC5424 by default) +func NewRemoteSyslog(cfg *LoggerConfig) (*RemoteSyslog, error) { + var err error + log.Info("NewSyslog logger: %v", cfg) + + sys := &RemoteSyslog{ + mu: &sync.RWMutex{}, + } + sys.Name = LOGGER_REMOTE_SYSLOG + sys.cfg = cfg + + // list of allowed formats for this logger + sys.logFormat = formats.NewRfc5424() + if cfg.Format == formats.RFC3164 { + sys.logFormat = formats.NewRfc3164() + } else if cfg.Format == formats.CSV { + sys.logFormat = formats.NewCSV() + } + + sys.Tag = logTag + if cfg.Tag != "" { + sys.Tag = cfg.Tag + } + sys.Hostname, err = os.Hostname() + if err != nil { + sys.Hostname = "localhost" + } + if cfg.WriteTimeout == "" { + cfg.WriteTimeout = writeTimeout + } + sys.Timeout, _ = time.ParseDuration(cfg.WriteTimeout) + + if err = sys.Open(); err != nil { + log.Error("Error loading logger: %s", err) + return nil, err + } + log.Info("[%s] initialized: %v", sys.Name, cfg) + + return sys, err +} + +// Open opens a new connection with a server or with the daemon. +func (s *RemoteSyslog) Open() (err error) { + atomic.StoreUint32(&s.errors, 0) + if s.cfg.Server == "" { + return fmt.Errorf("[%s] Server address must not be empty", s.Name) + } + s.mu.Lock() + s.netConn, err = s.Dial(s.cfg.Protocol, s.cfg.Server, s.Timeout*5) + s.mu.Unlock() + + if err == nil { + atomic.StoreUint32(&s.status, CONNECTED) + } + return err +} + +// Dial opens a new connection with a syslog server. +func (s *RemoteSyslog) Dial(proto, addr string, connTimeout time.Duration) (netConn net.Conn, err error) { + switch proto { + case "udp", "tcp": + netConn, err = net.DialTimeout(proto, addr, connTimeout) + if err != nil { + return nil, err + } + default: + return nil, fmt.Errorf("[%s] Network protocol %s not supported", s.Name, proto) + } + + return netConn, nil +} + +// Close closes the writer object +func (s *RemoteSyslog) Close() (err error) { + s.mu.RLock() + defer s.mu.RUnlock() + + if s.netConn != nil { + err = s.netConn.Close() + //s.netConn.conn = nil + } + atomic.StoreUint32(&s.status, DISCONNECTED) + return +} + +// ReOpen tries to reestablish the connection with the writer +func (s *RemoteSyslog) ReOpen() { + if atomic.LoadUint32(&s.status) == CONNECTING { + return + } + atomic.StoreUint32(&s.status, CONNECTING) + if err := s.Close(); err != nil { + log.Debug("[%s] error closing Close(): %s", s.Name, err) + } + + if err := s.Open(); err != nil { + log.Debug("[%s] ReOpen() error: %s", s.Name, err) + return + } +} + +// Transform transforms data for proper ingestion. +func (s *RemoteSyslog) Transform(args ...interface{}) (out string) { + if s.logFormat != nil { + args = append(args, s.Hostname) + args = append(args, s.Tag) + out = s.logFormat.Transform(args...) + } + return +} + +func (s *RemoteSyslog) Write(msg string) { + deadline := time.Now().Add(s.Timeout) + + // BUG: it's fairly common to have write timeouts via udp/tcp. + // Reopening the connection with the server helps to resume sending events to syslog, + // and have a continuous stream of events. Otherwise it'd stop working. + // I haven't figured out yet why these write errors ocurr. + s.mu.RLock() + s.netConn.SetWriteDeadline(deadline) + _, err := s.netConn.Write([]byte(msg)) + s.mu.RUnlock() + + if err != nil { + log.Debug("[%s] %s write error: %v", s.Name, s.cfg.Protocol, err.(net.Error)) + atomic.AddUint32(&s.errors, 1) + if atomic.LoadUint32(&s.errors) > maxAllowedErrors { + s.ReOpen() + return + } + } +} + +// https://cs.opensource.google/go/go/+/refs/tags/go1.18.2:src/log/syslog/syslog.go;l=286;drc=0a1a092c4b56a1d4033372fbd07924dad8cbb50b +func (s *RemoteSyslog) formatLine(msg string) string { + return msg +} diff --git a/daemon/log/loggers/syslog.go b/daemon/log/loggers/syslog.go new file mode 100644 index 0000000..d0ea929 --- /dev/null +++ b/daemon/log/loggers/syslog.go @@ -0,0 +1,79 @@ +package loggers + +import ( + "log/syslog" + + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/evilsocket/opensnitch/daemon/log/formats" +) + +const ( + LOGGER_SYSLOG = "syslog" +) + +// Syslog defines the logger that writes traces to the syslog. +// It can write to the local or a remote daemon. +type Syslog struct { + Name string + Writer *syslog.Writer + Tag string + logFormat formats.LoggerFormat + cfg *LoggerConfig +} + +// NewSyslog returns a new object that manipulates and prints outbound connections +// to syslog (local or remote), with the given format (RFC5424 by default) +func NewSyslog(cfg *LoggerConfig) (*Syslog, error) { + var err error + log.Info("NewSyslog logger: %v", cfg) + + sys := &Syslog{ + Name: LOGGER_SYSLOG, + cfg: cfg, + } + + sys.logFormat = formats.NewRfc5424() + if cfg.Format == formats.CSV { + sys.logFormat = formats.NewCSV() + } + + sys.Tag = logTag + if cfg.Tag != "" { + sys.Tag = cfg.Tag + } + + if err = sys.Open(); err != nil { + log.Error("Error loading logger: %s", err) + return nil, err + } + log.Info("[%s logger] initialized: %v", sys.Name, cfg) + + return sys, err +} + +// Open opens a new connection with a server or with the daemon. +func (s *Syslog) Open() error { + var err error + s.Writer, err = syslog.New(syslog.LOG_NOTICE|syslog.LOG_DAEMON, logTag) + + return err +} + +// Close closes the writer object +func (s *Syslog) Close() error { + return s.Writer.Close() +} + +// Transform transforms data for proper ingestion. +func (s *Syslog) Transform(args ...interface{}) (out string) { + if s.logFormat != nil { + out = s.logFormat.Transform(args...) + } + return +} + +func (s *Syslog) Write(msg string) { + if err := s.Writer.Notice(msg); err != nil { + log.Error("[%s] write error: %s", s.Name, err) + } +} diff --git a/daemon/main.go b/daemon/main.go new file mode 100644 index 0000000..fdad4cc --- /dev/null +++ b/daemon/main.go @@ -0,0 +1,619 @@ +/* Copyright (C) 2018 Simone Margaritelli +// 2021 themighty1 +// 2022 calesanz +// 2019-2022 Gustavo Iñiguez Goia +// +// This file is part of OpenSnitch. +// +// OpenSnitch is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// OpenSnitch is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with OpenSnitch. If not, see <http://www.gnu.org/licenses/>. +*/ + +package main + +import ( + "bytes" + "context" + "flag" + "fmt" + "io/ioutil" + golog "log" + "net" + "os" + "os/signal" + "runtime" + "runtime/pprof" + "syscall" + "time" + + "github.com/evilsocket/opensnitch/daemon/conman" + "github.com/evilsocket/opensnitch/daemon/core" + "github.com/evilsocket/opensnitch/daemon/dns" + "github.com/evilsocket/opensnitch/daemon/dns/systemd" + "github.com/evilsocket/opensnitch/daemon/firewall" + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/evilsocket/opensnitch/daemon/log/loggers" + "github.com/evilsocket/opensnitch/daemon/netfilter" + "github.com/evilsocket/opensnitch/daemon/netlink" + "github.com/evilsocket/opensnitch/daemon/procmon/ebpf" + "github.com/evilsocket/opensnitch/daemon/procmon/monitor" + "github.com/evilsocket/opensnitch/daemon/rule" + "github.com/evilsocket/opensnitch/daemon/statistics" + "github.com/evilsocket/opensnitch/daemon/ui" + "github.com/evilsocket/opensnitch/daemon/ui/config" + "github.com/evilsocket/opensnitch/daemon/ui/protocol" +) + +var ( + showVersion = false + checkRequirements = false + procmonMethod = "" + logFile = "" + logUTC = true + logMicro = false + rulesPath = "/etc/opensnitchd/rules/" + configFile = "/etc/opensnitchd/default-config.json" + ebpfModPath = "" // /usr/lib/opensnitchd/ebpf + noLiveReload = false + queueNum = 0 + repeatQueueNum int //will be set later to queueNum + 1 + workers = 16 + debug = false + warning = false + important = false + errorlog = false + + uiSocket = "" + uiClient = (*ui.Client)(nil) + + cpuProfile = "" + memProfile = "" + + ctx = (context.Context)(nil) + cancel = (context.CancelFunc)(nil) + err = (error)(nil) + rules = (*rule.Loader)(nil) + stats = (*statistics.Statistics)(nil) + queue = (*netfilter.Queue)(nil) + repeatPktChan = (<-chan netfilter.Packet)(nil) + pktChan = (<-chan netfilter.Packet)(nil) + wrkChan = (chan netfilter.Packet)(nil) + sigChan = (chan os.Signal)(nil) + exitChan = (chan bool)(nil) + loggerMgr *loggers.LoggerManager + resolvMonitor *systemd.ResolvedMonitor +) + +func init() { + flag.BoolVar(&showVersion, "version", debug, "Show daemon version of this executable and exit.") + flag.BoolVar(&checkRequirements, "check-requirements", debug, "Check system requirements for incompatibilities.") + + flag.StringVar(&procmonMethod, "process-monitor-method", procmonMethod, "How to search for processes path. Options: ftrace, audit (experimental), ebpf (experimental), proc (default)") + flag.StringVar(&uiSocket, "ui-socket", uiSocket, "Path the UI gRPC service listener (https://github.com/grpc/grpc/blob/master/doc/naming.md).") + flag.IntVar(&queueNum, "queue-num", queueNum, "Netfilter queue number.") + flag.IntVar(&workers, "workers", workers, "Number of concurrent workers.") + flag.BoolVar(&noLiveReload, "no-live-reload", debug, "Disable rules live reloading.") + + flag.StringVar(&rulesPath, "rules-path", rulesPath, "Path to load JSON rules from.") + flag.StringVar(&configFile, "config-file", configFile, "Path to the daemon configuration file.") + //flag.StringVar(&ebpfModPath, "ebpf-modules-path", ebpfModPath, "Path to the directory with the eBPF modules.") + flag.StringVar(&logFile, "log-file", logFile, "Write logs to this file instead of the standard output.") + flag.BoolVar(&logUTC, "log-utc", logUTC, "Write logs output with UTC timezone (enabled by default).") + flag.BoolVar(&logMicro, "log-micro", logMicro, "Write logs output with microsecond timestamp (disabled by default).") + flag.BoolVar(&debug, "debug", debug, "Enable debug level logs.") + flag.BoolVar(&warning, "warning", warning, "Enable warning level logs.") + flag.BoolVar(&important, "important", important, "Enable important level logs.") + flag.BoolVar(&errorlog, "error", errorlog, "Enable error level logs.") + + flag.StringVar(&cpuProfile, "cpu-profile", cpuProfile, "Write CPU profile to this file.") + flag.StringVar(&memProfile, "mem-profile", memProfile, "Write memory profile to this file.") +} + +// Load configuration file from disk, by default from /etc/opensnitchd/default-config.json, +// or from the path specified by configFile. +// This configuration will be loaded again by uiClient(), in order to monitor it for changes. +func loadDiskConfiguration() (*config.Config, error) { + if configFile == "" { + return nil, fmt.Errorf("Configuration file cannot be empty") + } + + raw, err := config.Load(configFile) + if err != nil || len(raw) == 0 { + return nil, fmt.Errorf("Error loading configuration %s: %s", configFile, err) + } + clientConfig, err := config.Parse(raw) + if err != nil { + return nil, fmt.Errorf("Error parsing configuration %s: %s", configFile, err) + } + + log.Info("Loading configuration file %s ...", configFile) + return &clientConfig, nil +} + +func overwriteLogging() bool { + return debug || warning || important || errorlog || logFile != "" || logMicro +} + +func setupLogging() { + golog.SetOutput(ioutil.Discard) + if debug { + log.SetLogLevel(log.DEBUG) + } else if warning { + log.SetLogLevel(log.WARNING) + } else if important { + log.SetLogLevel(log.IMPORTANT) + } else if errorlog { + log.SetLogLevel(log.ERROR) + } else { + log.SetLogLevel(log.INFO) + } + + log.SetLogUTC(logUTC) + log.SetLogMicro(logMicro) + + var logFileToUse string + if logFile == "" { + logFileToUse = log.StdoutFile + } else { + logFileToUse = logFile + } + log.Close() + if err := log.OpenFile(logFileToUse); err != nil { + log.Error("Error opening user defined log: %s %s", logFileToUse, err) + } +} + +func setupProfiling() { + if cpuProfile != "" { + if f, err := os.Create(cpuProfile); err != nil { + log.Fatal("%s", err) + } else if err := pprof.StartCPUProfile(f); err != nil { + log.Fatal("%s", err) + } + } +} + +func setupSignals() { + sigChan = make(chan os.Signal, 1) + exitChan = make(chan bool, workers+1) + signal.Notify(sigChan, + syscall.SIGHUP, + syscall.SIGINT, + syscall.SIGTERM, + syscall.SIGQUIT) + go func() { + sig := <-sigChan + log.Raw("\n") + log.Important("Got signal: %v", sig) + cancel() + time.AfterFunc(10*time.Second, func() { + log.Error("[REVIEW] closing due to timeout") + os.Exit(0) + }) + }() +} + +func worker(id int) { + log.Debug("Worker #%d started.", id) + for true { + select { + case <-ctx.Done(): + goto Exit + default: + pkt, ok := <-wrkChan + if !ok { + log.Debug("worker channel closed %d", id) + goto Exit + } + onPacket(pkt) + } + } +Exit: + log.Debug("worker #%d exit", id) +} + +func setupWorkers() { + log.Debug("Starting %d workers ...", workers) + // setup the workers + wrkChan = make(chan netfilter.Packet) + for i := 0; i < workers; i++ { + go worker(i) + } +} + +// Listen to events sent from other modules +func listenToEvents() { + for i := 0; i < 5; i++ { + go func(uiClient *ui.Client) { + for evt := range ebpf.Events() { + // for loop vars are per-loop, not per-item + evt := evt + uiClient.PostAlert( + protocol.Alert_WARNING, + protocol.Alert_KERNEL_EVENT, + protocol.Alert_SHOW_ALERT, + protocol.Alert_MEDIUM, + evt) + } + }(uiClient) + } +} + +func initSystemdResolvedMonitor() { + resolvMonitor, err := systemd.NewResolvedMonitor() + if err != nil { + log.Debug("[DNS] Unable to use systemd-resolved monitor: %s", err) + return + } + _, err = resolvMonitor.Connect() + if err != nil { + log.Debug("[DNS] Connecting to systemd-resolved: %s", err) + return + } + err = resolvMonitor.Subscribe() + if err != nil { + log.Debug("[DNS] Subscribing to systemd-resolved DNS events: %s", err) + return + } + go func() { + for { + select { + case exit := <-resolvMonitor.Exit(): + if exit == nil { + log.Info("[DNS] systemd-resolved monitor stopped") + return + } + log.Debug("[DNS] systemd-resolved monitor disconnected. Reconnecting...") + case response := <-resolvMonitor.GetDNSResponses(): + if response.State != systemd.SuccessState { + log.Debug("[DNS] systemd-resolved monitor response error: %v", response) + continue + } + /*for i, q := range response.Question { + log.Debug("%d SYSTEMD RESPONSE Q: %s", i, q.Name) + }*/ + for i, a := range response.Answer { + if a.RR.Key.Type != systemd.DNSTypeA && + a.RR.Key.Type != systemd.DNSTypeAAAA && + a.RR.Key.Type != systemd.DNSTypeCNAME { + log.Debug("systemd-resolved, excluding answer: %#v", a) + continue + } + domain := a.RR.Key.Name + ip := net.IP(a.RR.Address) + log.Debug("%d systemd-resolved monitor response: %s -> %s", i, domain, ip) + if a.RR.Key.Type == systemd.DNSTypeCNAME { + log.Debug("systemd-resolved CNAME >> %s -> %s", a.RR.Name, domain) + dns.Track(a.RR.Name, domain) + } else { + dns.Track(ip.String(), domain) + } + } + } + } + }() +} + +func doCleanup(queue, repeatQueue *netfilter.Queue) { + log.Info("Cleaning up ...") + firewall.Stop() + monitor.End() + uiClient.Close() + queue.Close() + repeatQueue.Close() + if resolvMonitor != nil { + resolvMonitor.Close() + } + + if cpuProfile != "" { + pprof.StopCPUProfile() + } + + if memProfile != "" { + f, err := os.Create(memProfile) + if err != nil { + fmt.Printf("Could not create memory profile: %s\n", err) + return + } + defer f.Close() + runtime.GC() // get up-to-date statistics + if err := pprof.WriteHeapProfile(f); err != nil { + fmt.Printf("Could not write memory profile: %s\n", err) + } + } +} + +func onPacket(packet netfilter.Packet) { + // DNS response, just parse, track and accept. + if dns.TrackAnswers(packet.Packet) == true { + packet.SetVerdictAndMark(netfilter.NF_ACCEPT, packet.Mark) + stats.OnDNSResponse() + return + } + + // Parse the connection state + con := conman.Parse(packet, uiClient.InterceptUnknown()) + if con == nil { + applyDefaultAction(&packet, nil) + return + } + // accept our own connections + if con.Process.ID == os.Getpid() { + packet.SetVerdict(netfilter.NF_ACCEPT) + return + } + + // search a match in preloaded rules + r := acceptOrDeny(&packet, con) + + if r != nil && r.Nolog { + return + } + // XXX: if a connection is not intercepted due to InterceptUnknown == false, + // it's not sent to the server, which leads to miss information. + stats.OnConnectionEvent(con, r, r == nil) +} + +func applyDefaultAction(packet *netfilter.Packet, con *conman.Connection) { + if uiClient.DefaultAction() == rule.Allow { + packet.SetVerdictAndMark(netfilter.NF_ACCEPT, packet.Mark) + return + } + if uiClient.DefaultAction() == rule.Reject && con != nil { + netlink.KillSocket(con.Protocol, con.SrcIP, con.SrcPort, con.DstIP, con.DstPort) + } + packet.SetVerdict(netfilter.NF_DROP) +} + +func acceptOrDeny(packet *netfilter.Packet, con *conman.Connection) *rule.Rule { + r := rules.FindFirstMatch(con) + if r == nil { + // no rule matched + // Note that as soon as we set a verdict on a packet, the next packet in the netfilter queue + // will begin to be processed even if this function hasn't yet returned + + // send a request to the UI client if + // 1) connected and running and 2) we are not already asking + if uiClient.Connected() == false || uiClient.GetIsAsking() == true { + applyDefaultAction(packet, con) + log.Debug("UI is not running or busy, connected: %v, running: %v", uiClient.Connected(), uiClient.GetIsAsking()) + return nil + } + + uiClient.SetIsAsking(true) + defer uiClient.SetIsAsking(false) + + // In order not to block packet processing, we send our packet to a different netfilter queue + // and then immediately pull it back out of that queue + packet.SetRequeueVerdict(uint16(repeatQueueNum)) + + var o bool + var pkt netfilter.Packet + // don't wait for the packet longer than 1 sec + select { + case pkt, o = <-repeatPktChan: + if !o { + log.Debug("error while receiving packet from repeatPktChan") + return nil + } + case <-time.After(1 * time.Second): + log.Debug("timed out while receiving packet from repeatPktChan") + return nil + } + + //check if the pulled out packet is the same we put in + if res := bytes.Compare(packet.Packet.Data(), pkt.Packet.Data()); res != 0 { + log.Error("The packet which was requeued has changed abruptly. This should never happen. Please report this incident to the Opensnitch developers. %v %v ", packet, pkt) + return nil + } + packet = &pkt + + // Update the hostname again. + // This is required due to a race between the ebpf dns hook and the actual first packet beeing sent + if con.DstHost == "" { + con.DstHost = dns.HostOr(con.DstIP, con.DstHost) + } + + r = uiClient.Ask(con) + if r == nil { + log.Error("Invalid rule received, applying default action") + applyDefaultAction(packet, con) + return nil + } + ok := false + pers := "" + action := string(r.Action) + if r.Action == rule.Allow { + action = log.Green(action) + } else { + action = log.Red(action) + } + + // check if and how the rule needs to be saved + if r.Duration == rule.Always { + pers = "Saved" + // add to the loaded rules and persist on disk + if err := rules.Add(r, true); err != nil { + log.Error("Error while saving rule: %s", err) + } else { + ok = true + } + } else { + pers = "Added" + // add to the rules but do not save to disk + if err := rules.Add(r, false); err != nil { + log.Error("Error while adding rule: %s", err) + } else { + ok = true + } + } + + if ok { + log.Important("%s new rule: %s if %s", pers, action, r.Operator.String()) + } + + } + if packet == nil { + log.Debug("Packet nil after processing rules") + return r + } + + if r.Enabled == false { + applyDefaultAction(packet, con) + ruleName := log.Green(r.Name) + log.Info("DISABLED (%s) %s %s -> %s:%d (%s)", uiClient.DefaultAction(), log.Bold(log.Green("✔")), log.Bold(con.Process.Path), log.Bold(con.To()), con.DstPort, ruleName) + + } else if r.Action == rule.Allow { + packet.SetVerdictAndMark(netfilter.NF_ACCEPT, packet.Mark) + ruleName := log.Green(r.Name) + if r.Operator.Operand == rule.OpTrue { + ruleName = log.Dim(r.Name) + } + log.Debug("%s %s -> %d:%s => %s:%d, mark: %x (%s)", log.Bold(log.Green("✔")), log.Bold(con.Process.Path), con.SrcPort, log.Bold(con.SrcIP.String()), log.Bold(con.To()), con.DstPort, packet.Mark, ruleName) + } else { + if r.Action == rule.Reject { + netlink.KillSocket(con.Protocol, con.SrcIP, con.SrcPort, con.DstIP, con.DstPort) + } + packet.SetVerdict(netfilter.NF_DROP) + + log.Debug("%s %s -> %d:%s => %s:%d, mark: %x (%s)", log.Bold(log.Red("✘")), log.Bold(con.Process.Path), con.SrcPort, log.Bold(con.SrcIP.String()), log.Bold(con.To()), con.DstPort, packet.Mark, log.Red(r.Name)) + } + + return r +} + +func main() { + ctx, cancel = context.WithCancel(context.Background()) + defer cancel() + flag.Parse() + + if showVersion { + fmt.Println(core.Version) + os.Exit(0) + } + if checkRequirements { + core.CheckSysRequirements() + os.Exit(0) + } + + setupLogging() + setupProfiling() + + log.Important("Starting %s v%s", core.Name, core.Version) + + cfg, err := loadDiskConfiguration() + if err != nil { + log.Fatal("%s", err) + } + if err == nil && cfg.Rules.Path != "" { + rulesPath = cfg.Rules.Path + } + if rulesPath == "" { + log.Fatal("rules path cannot be empty") + } + + rulesPath, err := core.ExpandPath(rulesPath) + if err != nil { + log.Fatal("Error accessing rules path (does it exist?): %s", err) + } + + setupSignals() + + log.Info("Loading rules from %s ...", rulesPath) + rules, err = rule.NewLoader(!noLiveReload) + if err != nil { + log.Fatal("%s", err) + } else if err = rules.Load(rulesPath); err != nil { + log.Fatal("%s", err) + } + stats = statistics.New(rules) + loggerMgr = loggers.NewLoggerManager() + uiClient = ui.NewClient(uiSocket, configFile, stats, rules, loggerMgr) + + // prepare the queue + setupWorkers() + queue, err := netfilter.NewQueue(uint16(queueNum)) + if err != nil { + msg := fmt.Sprintf("Error creating queue #%d: %s", queueNum, err) + uiClient.SendWarningAlert(msg) + log.Warning("Is opensnitchd already running?") + log.Fatal(msg) + } + pktChan = queue.Packets() + + repeatQueueNum = queueNum + 1 + repeatQueue, rqerr := netfilter.NewQueue(uint16(repeatQueueNum)) + if rqerr != nil { + msg := fmt.Sprintf("Error creating repeat queue #%d: %s", repeatQueueNum, rqerr) + uiClient.SendErrorAlert(msg) + log.Warning("Is opensnitchd already running?") + log.Warning(msg) + } + repeatPktChan = repeatQueue.Packets() + + // queue is ready, run firewall rules and start intercepting connections + if err = firewall.Init(uiClient.GetFirewallType(), &queueNum); err != nil { + log.Warning("%s", err) + uiClient.SendWarningAlert(err) + } + + uiClient.Connect() + listenToEvents() + + if overwriteLogging() { + setupLogging() + } + // overwrite monitor method from configuration if the user has passed + // the option via command line. + if procmonMethod != "" { + if err := monitor.ReconfigureMonitorMethod(procmonMethod, cfg.Ebpf.ModulesPath); err != nil { + msg := fmt.Sprintf("Unable to set process monitor method via parameter: %v", err) + uiClient.SendWarningAlert(msg) + log.Warning(msg) + } + } + + go func(uiClient *ui.Client, ebpfPath string) { + if err := dns.ListenerEbpf(ebpfPath); err != nil { + msg := fmt.Sprintf("EBPF-DNS: Unable to attach ebpf listener: %s", err) + log.Warning(msg) + // don't display an alert, since this module is not critical + uiClient.PostAlert( + protocol.Alert_ERROR, + protocol.Alert_GENERIC, + protocol.Alert_SAVE_TO_DB, + protocol.Alert_MEDIUM, + msg) + + } + }(uiClient, cfg.Ebpf.ModulesPath) + + initSystemdResolvedMonitor() + + log.Info("Running on netfilter queue #%d ...", queueNum) + for { + select { + case <-ctx.Done(): + goto Exit + case pkt, ok := <-pktChan: + if !ok { + goto Exit + } + wrkChan <- pkt + } + } +Exit: + close(wrkChan) + doCleanup(queue, repeatQueue) + os.Exit(0) +} diff --git a/daemon/netfilter/packet.go b/daemon/netfilter/packet.go new file mode 100644 index 0000000..7c3e95b --- /dev/null +++ b/daemon/netfilter/packet.go @@ -0,0 +1,62 @@ +package netfilter + +import "C" + +import ( + "github.com/google/gopacket" +) + +// packet consts +const ( + IPv4 = 4 +) + +// Verdict holds the action to perform on a packet (NF_DROP, NF_ACCEPT, etc) +type Verdict C.uint + +// VerdictContainer struct +type VerdictContainer struct { + Mark uint32 + Verdict Verdict + Packet []byte +} + +// Packet holds the data of a network packet +type Packet struct { + Packet gopacket.Packet + verdictChannel chan VerdictContainer + IfaceInIdx int + IfaceOutIdx int + Mark uint32 + UID uint32 + NetworkProtocol uint8 +} + +// SetVerdict emits a veredict on a packet +func (p *Packet) SetVerdict(v Verdict) { + p.verdictChannel <- VerdictContainer{Verdict: v, Packet: nil, Mark: 0} +} + +// SetVerdictAndMark emits a veredict on a packet and marks it in order to not +// analyze it again. +func (p *Packet) SetVerdictAndMark(v Verdict, mark uint32) { + p.verdictChannel <- VerdictContainer{Verdict: v, Packet: nil, Mark: mark} +} + +// SetRequeueVerdict apply a verdict on a requeued packet +func (p *Packet) SetRequeueVerdict(newQueueID uint16) { + v := uint(NF_QUEUE) + q := (uint(newQueueID) << 16) + v = v | q + p.verdictChannel <- VerdictContainer{Verdict: Verdict(v), Packet: nil, Mark: p.Mark} +} + +// SetVerdictWithPacket apply a verdict, but with a new packet +func (p *Packet) SetVerdictWithPacket(v Verdict, packet []byte) { + p.verdictChannel <- VerdictContainer{Verdict: v, Packet: packet, Mark: 0} +} + +// IsIPv4 returns if the packet is IPv4 +func (p *Packet) IsIPv4() bool { + return p.NetworkProtocol == IPv4 +} diff --git a/daemon/netfilter/queue.c b/daemon/netfilter/queue.c new file mode 100644 index 0000000..f2b7ef6 --- /dev/null +++ b/daemon/netfilter/queue.c @@ -0,0 +1,2 @@ +#include "queue.h" + diff --git a/daemon/netfilter/queue.go b/daemon/netfilter/queue.go new file mode 100644 index 0000000..ac4cf7a --- /dev/null +++ b/daemon/netfilter/queue.go @@ -0,0 +1,246 @@ +package netfilter + +/* +#cgo pkg-config: libnetfilter_queue +#cgo CFLAGS: -I/usr/include +#cgo LDFLAGS: -L/usr/lib64/ -ldl + +#include "queue.h" +*/ +import "C" + +import ( + "fmt" + "os" + "sync" + "syscall" + "time" + "unsafe" + + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + "golang.org/x/sys/unix" +) + +const ( + AF_INET = 2 + AF_INET6 = 10 + + NF_DROP Verdict = 0 + NF_ACCEPT Verdict = 1 + NF_STOLEN Verdict = 2 + NF_QUEUE Verdict = 3 + NF_REPEAT Verdict = 4 + NF_STOP Verdict = 5 + + NF_DEFAULT_QUEUE_SIZE uint32 = 4096 + NF_DEFAULT_PACKET_SIZE uint32 = 4096 +) + +var ( + queueIndex = make(map[uint32]*chan Packet, 0) + queueIndexLock = sync.RWMutex{} + + gopacketDecodeOptions = gopacket.DecodeOptions{Lazy: true, NoCopy: true} +) + +// VerdictContainerC is the struct that contains the mark, action, length and +// payload of a packet. +// It's defined in queue.h, and filled on go_callback() +type VerdictContainerC C.verdictContainer + +// Queue holds the information of a netfilter queue. +// The handles of the connection to the kernel and the created queue. +// A channel where the intercepted packets will be received. +// The ID of the queue. +type Queue struct { + h *C.struct_nfq_handle + qh *C.struct_nfq_q_handle + packets chan Packet + fd C.int + idx uint32 +} + +// NewQueue opens a new netfilter queue to receive packets marked with a mark. +func NewQueue(queueID uint16) (q *Queue, err error) { + q = &Queue{ + idx: uint32(time.Now().UnixNano()), + packets: make(chan Packet), + } + + if err = q.create(queueID); err != nil { + return nil, err + } else if err = q.setup(); err != nil { + return nil, err + } + + go q.run() + + return q, nil +} + +func (q *Queue) create(queueID uint16) (err error) { + var ret C.int + + if q.h, err = C.nfq_open(); err != nil { + return fmt.Errorf("Error opening Queue handle: %v", err) + } else if ret, err = C.nfq_unbind_pf(q.h, AF_INET); err != nil || ret < 0 { + errmsg := fmt.Errorf("Error %d unbinding existing q handler from AF_INET protocol family: %v", ret, err) + if syscall.Errno(ret) == unix.EINVAL { + errmsg = fmt.Errorf("%s\nRestarting your computer may help to solve this error (see issues: #323 and #912 for more information)", errmsg) + } + return errmsg + } else if ret, err = C.nfq_unbind_pf(q.h, AF_INET6); err != nil || ret < 0 { + return fmt.Errorf("Error (%d) unbinding existing q handler from AF_INET6 protocol family: %v", ret, err) + } else if ret, err := C.nfq_bind_pf(q.h, AF_INET); err != nil || ret < 0 { + return fmt.Errorf("Error (%d) binding to AF_INET protocol family: %v", ret, err) + } else if ret, err := C.nfq_bind_pf(q.h, AF_INET6); err != nil || ret < 0 { + return fmt.Errorf("Error (%d) binding to AF_INET6 protocol family: %v", ret, err) + } else if q.qh, err = C.CreateQueue(q.h, C.uint16_t(queueID), C.uint32_t(q.idx)); err != nil || q.qh == nil { + q.destroy() + return fmt.Errorf("Error binding to queue: %v", err) + } + + queueIndexLock.Lock() + queueIndex[q.idx] = &q.packets + queueIndexLock.Unlock() + + return nil +} + +func (q *Queue) setup() (err error) { + var ret C.int + + queueSize := C.uint32_t(NF_DEFAULT_QUEUE_SIZE) + bufferSize := C.uint(NF_DEFAULT_PACKET_SIZE) + totSize := C.uint(NF_DEFAULT_QUEUE_SIZE * NF_DEFAULT_PACKET_SIZE) + + if ret, err = C.nfq_set_queue_maxlen(q.qh, queueSize); err != nil || ret < 0 { + q.destroy() + return fmt.Errorf("Unable to set max packets in queue: %v", err) + } else if C.nfq_set_mode(q.qh, C.uint8_t(2), bufferSize) < 0 { + q.destroy() + return fmt.Errorf("Unable to set packets copy mode: %v", err) + } else if q.fd, err = C.nfq_fd(q.h); err != nil { + q.destroy() + return fmt.Errorf("Unable to get queue file-descriptor. %v", err) + } else if C.nfnl_rcvbufsiz(C.nfq_nfnlh(q.h), totSize) < 0 { + q.destroy() + return fmt.Errorf("Unable to increase netfilter buffer space size") + } + + return nil +} + +func (q *Queue) run() { + if errno := C.Run(q.h, q.fd); errno != 0 { + fmt.Fprintf(os.Stderr, "Terminating, unable to receive packet due to errno=%d", errno) + } +} + +// Close ensures that nfqueue resources are freed and closed. +// C.stop_reading_packets() stops the reading packets loop, which causes +// go-subroutine run() to exit. +// After exit, listening queue is destroyed and closed. +// If for some reason any of the steps stucks while closing it, we'll exit by timeout. +func (q *Queue) Close() { + C.stop_reading_packets() + q.destroy() + queueIndexLock.Lock() + delete(queueIndex, q.idx) + queueIndexLock.Unlock() + close(q.packets) +} + +func (q *Queue) destroy() { + // we'll try to exit cleanly, but sometimes nfqueue gets stuck + time.AfterFunc(5*time.Second, func() { + log.Warning("queue (%d) stuck, closing by timeout", q.idx) + if q != nil { + C.close(q.fd) + q.closeNfq() + } + os.Exit(0) + }) + if q.qh != nil { + if ret := C.nfq_destroy_queue(q.qh); ret != 0 { + log.Warning("Queue.destroy() idx=%d, nfq_destroy_queue() not closed: %d", q.idx, ret) + } + } + + q.closeNfq() +} + +func (q *Queue) closeNfq() { + if q.h != nil { + if ret := C.nfq_close(q.h); ret != 0 { + log.Warning("Queue.destroy() idx=%d, nfq_close() not closed: %d", q.idx, ret) + } + } +} + +// Packets return the list of enqueued packets. +func (q *Queue) Packets() <-chan Packet { + return q.packets +} + +// FYI: the export keyword is mandatory to specify that go_callback is defined elsewhere + +//export go_callback +func go_callback(queueID C.int, data *C.uchar, length C.int, mark C.uint, idx uint32, vc *VerdictContainerC, uid, devIn, devOut uint32) { + (*vc).verdict = C.uint(NF_ACCEPT) + (*vc).data = nil + (*vc).mark_set = 0 + (*vc).length = 0 + + queueIndexLock.RLock() + queueChannel, found := queueIndex[idx] + queueIndexLock.RUnlock() + if !found { + fmt.Fprintf(os.Stderr, "Unexpected queue idx %d\n", idx) + return + } + + xdata := C.GoBytes(unsafe.Pointer(data), length) + + p := Packet{ + verdictChannel: make(chan VerdictContainer), + Mark: uint32(mark), + UID: uid, + NetworkProtocol: xdata[0] >> 4, // first 4 bits is the version + IfaceInIdx: int(devIn), + IfaceOutIdx: int(devOut), + } + + var packet gopacket.Packet + if p.IsIPv4() { + packet = gopacket.NewPacket(xdata, layers.LayerTypeIPv4, gopacketDecodeOptions) + } else { + packet = gopacket.NewPacket(xdata, layers.LayerTypeIPv6, gopacketDecodeOptions) + } + + p.Packet = packet + + select { + case *queueChannel <- p: + select { + case v := <-p.verdictChannel: + if v.Packet == nil { + (*vc).verdict = C.uint(v.Verdict) + } else { + (*vc).verdict = C.uint(v.Verdict) + (*vc).data = (*C.uchar)(unsafe.Pointer(&v.Packet[0])) + (*vc).length = C.uint(len(v.Packet)) + } + + if v.Mark != 0 { + (*vc).mark_set = C.uint(1) + (*vc).mark = C.uint(v.Mark) + } + } + + case <-time.After(1 * time.Millisecond): + fmt.Fprintf(os.Stderr, "Timed out while sending packet to queue channel %d\n", idx) + } +} diff --git a/daemon/netfilter/queue.h b/daemon/netfilter/queue.h new file mode 100644 index 0000000..9c06aab --- /dev/null +++ b/daemon/netfilter/queue.h @@ -0,0 +1,117 @@ +#ifndef _NETFILTER_QUEUE_H +#define _NETFILTER_QUEUE_H + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <errno.h> +#include <math.h> +#include <unistd.h> +#include <dlfcn.h> +#include <netinet/in.h> +#include <linux/types.h> +#include <linux/socket.h> +#include <linux/netfilter.h> +#include <libnetfilter_queue/libnetfilter_queue.h> + +typedef struct { + unsigned int verdict; + unsigned int mark; + unsigned int mark_set; + unsigned int length; + unsigned char *data; +} verdictContainer; + +static void *get_uid = NULL; + +extern void go_callback(int id, unsigned char* data, int len, unsigned int mark, uint32_t idx, verdictContainer *vc, uint32_t uid, uint32_t in_dev, uint32_t out_dev); + +static uint8_t stop = 0; + +static inline void configure_uid_if_available(struct nfq_q_handle *qh){ + void *hndl = dlopen("libnetfilter_queue.so.1", RTLD_LAZY); + if (!hndl) { + hndl = dlopen("libnetfilter_queue.so", RTLD_LAZY); + if (!hndl){ + printf("WARNING: libnetfilter_queue not available\n"); + return; + } + } + if ((get_uid = dlsym(hndl, "nfq_get_uid")) == NULL){ + printf("WARNING: nfq_get_uid not available\n"); + return; + } + printf("OK: libnetfiler_queue supports nfq_get_uid\n"); +#ifdef NFQA_CFG_F_UID_GID + if (qh != NULL && nfq_set_queue_flags(qh, NFQA_CFG_F_UID_GID, NFQA_CFG_F_UID_GID)){ + printf("WARNING: UID not available on this kernel/libnetfilter_queue\n"); + } +#endif +} + +static int nf_callback(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa, void *arg){ + if (stop) { + return -1; + } + + uint32_t id = -1, idx = 0, mark = 0; + struct nfqnl_msg_packet_hdr *ph = NULL; + unsigned char *buffer = NULL; + int size = 0; + verdictContainer vc = {0}; + uint32_t uid = 0xffffffff; + uint32_t in_dev=0, out_dev=0; + + in_dev = nfq_get_indev(nfa); + out_dev = nfq_get_outdev(nfa); + + mark = nfq_get_nfmark(nfa); + ph = nfq_get_msg_packet_hdr(nfa); + id = ntohl(ph->packet_id); + size = nfq_get_payload(nfa, &buffer); + idx = (uint32_t)((uintptr_t)arg); + +#ifdef NFQA_CFG_F_UID_GID + if (get_uid) + nfq_get_uid(nfa, &uid); +#endif + + go_callback(id, buffer, size, mark, idx, &vc, uid, in_dev, out_dev); + + if( vc.mark_set == 1 ) { + return nfq_set_verdict2(qh, id, vc.verdict, vc.mark, vc.length, vc.data); + } + return nfq_set_verdict2(qh, id, vc.verdict, vc.mark, vc.length, vc.data); +} + +static inline struct nfq_q_handle* CreateQueue(struct nfq_handle *h, uint16_t queue, uint32_t idx) { + struct nfq_q_handle* qh = nfq_create_queue(h, queue, &nf_callback, (void*)((uintptr_t)idx)); + if (qh == NULL){ + printf("ERROR: nfq_create_queue() queue not created\n"); + } else { + configure_uid_if_available(qh); + } + return qh; +} + +static inline void stop_reading_packets() { + stop = 1; +} + +static inline int Run(struct nfq_handle *h, int fd) { + char buf[4096] __attribute__ ((aligned)); + int rcvd, opt = 1; + + setsockopt(fd, SOL_NETLINK, NETLINK_NO_ENOBUFS, &opt, sizeof(int)); + + while ((rcvd = recv(fd, buf, sizeof(buf), 0)) >= 0) { + if (stop == 1) { + return errno; + } + nfq_handle_packet(h, buf, rcvd); + } + + return errno; +} + +#endif diff --git a/daemon/netlink/ifaces.go b/daemon/netlink/ifaces.go new file mode 100644 index 0000000..d96d8ed --- /dev/null +++ b/daemon/netlink/ifaces.go @@ -0,0 +1,47 @@ +package netlink + +import ( + "net" + + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/vishvananda/netlink" +) + +// https://cs.opensource.google/go/go/+/refs/tags/go1.20.6:src/net/ip.go;l=133 +// TODO: remove when upgrading go version. +func isPrivate(ip net.IP) bool { + if ip4 := ip.To4(); ip4 != nil { + return ip4[0] == 10 || + (ip4[0] == 172 && ip4[1]&0xf0 == 16) || + (ip4[0] == 192 && ip4[1] == 168) + } + return len(ip) == 16 && ip[0]&0xfe == 0xfc +} + +// GetLocalAddrs returns the list of local IPs +func GetLocalAddrs() map[string]netlink.Addr { + localAddresses := make(map[string]netlink.Addr) + addr, err := netlink.AddrList(nil, netlink.FAMILY_ALL) + if err != nil { + log.Error("eBPF error looking up this machine's addresses via netlink: %v", err) + return nil + } + for _, a := range addr { + log.Debug("local addr: %+v\n", a) + localAddresses[a.IP.String()] = a + } + + return localAddresses +} + +// AddrUpdateToAddr translates AddrUpdate struct to Addr. +func AddrUpdateToAddr(addr *netlink.AddrUpdate) netlink.Addr { + return netlink.Addr{ + IPNet: &addr.LinkAddress, + LinkIndex: addr.LinkIndex, + Flags: addr.Flags, + Scope: addr.Scope, + PreferedLft: addr.PreferedLft, + ValidLft: addr.ValidLft, + } +} diff --git a/daemon/netlink/socket.go b/daemon/netlink/socket.go new file mode 100644 index 0000000..73a04aa --- /dev/null +++ b/daemon/netlink/socket.go @@ -0,0 +1,230 @@ +package netlink + +import ( + "fmt" + "net" + "strconv" + "syscall" + + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/vishvananda/netlink" + "golang.org/x/sys/unix" +) + +// GetSocketInfo asks the kernel via netlink for a given connection. +// If the connection is found, we return the uid and the possible +// associated inodes. +// If the outgoing connection is not found but there're entries with the source +// port and same protocol, add all the inodes to the list. +// +// Some examples: +// outgoing connection as seen by netfilter || connection details dumped from kernel +// +// 47344:192.168.1.106 -> 151.101.65.140:443 || in kernel: 47344:192.168.1.106 -> 151.101.65.140:443 +// 8612:192.168.1.5 -> 192.168.1.255:8612 || in kernel: 8612:192.168.1.105 -> 0.0.0.0:0 +// 123:192.168.1.5 -> 217.144.138.234:123 || in kernel: 123:0.0.0.0 -> 0.0.0.0:0 +// 45015:127.0.0.1 -> 239.255.255.250:1900 || in kernel: 45015:127.0.0.1 -> 0.0.0.0:0 +// 50416:fe80::9fc2:ddcf:df22:aa50 -> fe80::1:53 || in kernel: 50416:254.128.0.0 -> 254.128.0.0:53 +// 51413:192.168.1.106 -> 103.224.182.250:1337 || in kernel: 51413:0.0.0.0 -> 0.0.0.0:0 +func GetSocketInfo(proto string, srcIP net.IP, srcPort uint, dstIP net.IP, dstPort uint) (uid int, inodes []int) { + uid = -1 + family := uint8(syscall.AF_INET) + ipproto := uint8(syscall.IPPROTO_TCP) + protoLen := len(proto) + if proto[protoLen-1:protoLen] == "6" { + family = syscall.AF_INET6 + } + + if proto[:3] == "udp" { + ipproto = syscall.IPPROTO_UDP + if protoLen >= 7 && proto[:7] == "udplite" { + ipproto = syscall.IPPROTO_UDPLITE + } + } + if protoLen >= 4 && proto[:4] == "sctp" { + ipproto = syscall.IPPROTO_SCTP + } + if protoLen >= 4 && proto[:4] == "icmp" { + ipproto = syscall.IPPROTO_RAW + } + if sockList, err := SocketGet(family, ipproto, uint16(srcPort), uint16(dstPort), srcIP, dstIP); err == nil { + for n, sock := range sockList { + if sock.UID != 0xffffffff { + uid = int(sock.UID) + } + log.Debug("[%d/%d] outgoing connection uid: %d, %d:%v -> %v:%d || netlink response: %d:%v -> %v:%d inode: %d - loopback: %v multicast: %v unspecified: %v linklocalunicast: %v ifaceLocalMulticast: %v GlobalUni: %v ", + n, len(sockList), + int(sock.UID), + srcPort, srcIP, dstIP, dstPort, + sock.ID.SourcePort, sock.ID.Source, + sock.ID.Destination, sock.ID.DestinationPort, sock.INode, + sock.ID.Destination.IsLoopback(), + sock.ID.Destination.IsMulticast(), + sock.ID.Destination.IsUnspecified(), + sock.ID.Destination.IsLinkLocalUnicast(), + sock.ID.Destination.IsLinkLocalMulticast(), + sock.ID.Destination.IsGlobalUnicast(), + ) + + if sock.ID.SourcePort == uint16(srcPort) && sock.ID.Source.Equal(srcIP) && + (sock.ID.DestinationPort == uint16(dstPort)) && + ((sock.ID.Destination.IsGlobalUnicast() || sock.ID.Destination.IsLoopback()) && sock.ID.Destination.Equal(dstIP)) { + inodes = append([]int{int(sock.INode)}, inodes...) + continue + } + log.Debug("GetSocketInfo() invalid: %d:%v -> %v:%d", sock.ID.SourcePort, sock.ID.Source, sock.ID.Destination, sock.ID.DestinationPort) + } + + // handle special cases (see function description): ntp queries (123), broadcasts, incomming connections. + if len(inodes) == 0 && len(sockList) > 0 { + for n, sock := range sockList { + if sockList[n].ID.Destination.Equal(net.IPv4zero) || sockList[n].ID.Destination.Equal(net.IPv6zero) { + inodes = append([]int{int(sock.INode)}, inodes...) + log.Debug("netlink socket not found, adding entry: %d:%v -> %v:%d || %d:%v -> %v:%d inode: %d state: %s", + srcPort, srcIP, dstIP, dstPort, + sockList[n].ID.SourcePort, sockList[n].ID.Source, + sockList[n].ID.Destination, sockList[n].ID.DestinationPort, + sockList[n].INode, TCPStatesMap[sock.State]) + } else if sock.ID.SourcePort == uint16(srcPort) && sock.ID.Source.Equal(srcIP) && + (sock.ID.DestinationPort == uint16(dstPort)) { + inodes = append([]int{int(sock.INode)}, inodes...) + continue + } else { + log.Debug("netlink socket not found, EXCLUDING entry: %d:%v -> %v:%d || %d:%v -> %v:%d inode: %d state: %s", + srcPort, srcIP, dstIP, dstPort, + sockList[n].ID.SourcePort, sockList[n].ID.Source, + sockList[n].ID.Destination, sockList[n].ID.DestinationPort, + sockList[n].INode, TCPStatesMap[sock.State]) + } + } + } + } else { + log.Debug("netlink socket error: %v - %d:%v -> %v:%d", err, srcPort, srcIP, dstIP, dstPort) + } + + return uid, inodes +} + +// GetSocketInfoByInode dumps the kernel sockets table and searches the given +// inode on it. +func GetSocketInfoByInode(inodeStr string) (*Socket, error) { + inode, err := strconv.ParseUint(inodeStr, 10, 32) + if err != nil { + return nil, err + } + + type inetStruct struct{ family, proto uint8 } + socketTypes := []inetStruct{ + {syscall.AF_INET, syscall.IPPROTO_TCP}, + {syscall.AF_INET, syscall.IPPROTO_UDP}, + {syscall.AF_INET6, syscall.IPPROTO_TCP}, + {syscall.AF_INET6, syscall.IPPROTO_UDP}, + } + + for _, socket := range socketTypes { + socketList, err := SocketsDump(socket.family, socket.proto) + if err != nil { + return nil, err + } + for idx := range socketList { + if uint32(inode) == socketList[idx].INode { + return socketList[idx], nil + } + } + } + return nil, fmt.Errorf("Inode not found") +} + +// KillSocket kills a socket given the properties of a connection. +func KillSocket(proto string, srcIP net.IP, srcPort uint, dstIP net.IP, dstPort uint) { + family := uint8(syscall.AF_INET) + ipproto := uint8(syscall.IPPROTO_TCP) + protoLen := len(proto) + if proto[protoLen-1:protoLen] == "6" { + family = syscall.AF_INET6 + } + + if proto[:3] == "udp" { + ipproto = syscall.IPPROTO_UDP + if protoLen >= 7 && proto[:7] == "udplite" { + ipproto = syscall.IPPROTO_UDPLITE + } + } + + if sockList, err := SocketGet(family, ipproto, uint16(srcPort), uint16(dstPort), srcIP, dstIP); err == nil { + for _, s := range sockList { + if err := SocketKill(family, ipproto, s.ID); err != nil { + log.Debug("Unable to kill socket: %d, %d, %v", srcPort, dstPort, err) + } + } + } +} + +// KillSockets kills all sockets given a family and a protocol. +// Be careful if you don't exclude local sockets, many local servers may misbehave, +// entering in an infinite loop. +func KillSockets(fam, proto uint8, excludeLocal bool) error { + sockListTCP, err := SocketsDump(fam, proto) + if err != nil { + return fmt.Errorf("eBPF could not dump TCP (%d/%d) sockets via netlink: %v", fam, proto, err) + } + + for _, sock := range sockListTCP { + if excludeLocal && (isPrivate(sock.ID.Destination) || + sock.ID.Source.IsUnspecified() || + sock.ID.Destination.IsUnspecified()) { + continue + } + if err := SocketKill(fam, proto, sock.ID); err != nil { + log.Debug("Unable to kill socket (%+v): %s", sock.ID, err) + } + } + + return nil +} + +// KillAllSockets kills the sockets for the given families and protocols. +func KillAllSockets() { + type opts struct { + fam uint8 + proto uint8 + } + optList := []opts{ + // add families and protos as wish + {unix.AF_INET, uint8(syscall.IPPROTO_TCP)}, + {unix.AF_INET6, uint8(syscall.IPPROTO_TCP)}, + {unix.AF_INET, uint8(syscall.IPPROTO_UDP)}, + {unix.AF_INET6, uint8(syscall.IPPROTO_UDP)}, + {unix.AF_INET, uint8(syscall.IPPROTO_SCTP)}, + {unix.AF_INET6, uint8(syscall.IPPROTO_SCTP)}, + } + for _, opt := range optList { + KillSockets(opt.fam, opt.proto, true) + } + +} + +// FlushConnections flushes conntrack as soon as netfilter rule is set. +// This ensures that already-established connections will go to netfilter queue. +func FlushConnections() { + if err := netlink.ConntrackTableFlush(netlink.ConntrackTable); err != nil { + log.Error("error flushing ConntrackTable %s", err) + } + if err := netlink.ConntrackTableFlush(netlink.ConntrackExpectTable); err != nil { + log.Error("error flusing ConntrackExpectTable %s", err) + } + + // Force established connections to reestablish again. + KillAllSockets() +} + +// SocketsAreEqual compares 2 different sockets to see if they match. +func SocketsAreEqual(aSocket, bSocket *Socket) bool { + return ((*aSocket).INode == (*bSocket).INode && + //inodes are unique enough, so the matches below will never have to be checked + (*aSocket).ID.SourcePort == (*bSocket).ID.SourcePort && + (*aSocket).ID.Source.Equal((*bSocket).ID.Source) && + (*aSocket).ID.Destination.Equal((*bSocket).ID.Destination) && + (*aSocket).ID.DestinationPort == (*bSocket).ID.DestinationPort && + (*aSocket).UID == (*bSocket).UID) +} diff --git a/daemon/netlink/socket_linux.go b/daemon/netlink/socket_linux.go new file mode 100644 index 0000000..caf1d53 --- /dev/null +++ b/daemon/netlink/socket_linux.go @@ -0,0 +1,264 @@ +package netlink + +import ( + "encoding/binary" + "errors" + "fmt" + "net" + "syscall" + + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/vishvananda/netlink/nl" +) + +// This is a modification of https://github.com/vishvananda/netlink socket_linux.go - Apache2.0 license +// which adds support for query UDP, UDPLITE and IPv6 sockets to SocketGet() + +const ( + SOCK_DESTROY = 21 + sizeofSocketID = 0x30 + sizeofSocketRequest = sizeofSocketID + 0x8 + sizeofSocket = sizeofSocketID + 0x18 +) + +var ( + native = nl.NativeEndian() + networkOrder = binary.BigEndian + TCP_ALL = uint32(0xfff) +) + +// https://elixir.bootlin.com/linux/latest/source/include/net/tcp_states.h +const ( + TCP_INVALID = iota + TCP_ESTABLISHED + TCP_SYN_SENT + TCP_SYN_RECV + TCP_FIN_WAIT1 + TCP_FIN_WAIT2 + TCP_TIME_WAIT + TCP_CLOSE + TCP_CLOSE_WAIT + TCP_LAST_ACK + TCP_LISTEN + TCP_CLOSING + TCP_NEW_SYN_REC + TCP_MAX_STATES +) + +// TCPStatesMap holds the list of TCP states +var TCPStatesMap = map[uint8]string{ + TCP_INVALID: "invalid", + TCP_ESTABLISHED: "established", + TCP_SYN_SENT: "syn_sent", + TCP_SYN_RECV: "syn_recv", + TCP_FIN_WAIT1: "fin_wait1", + TCP_FIN_WAIT2: "fin_wait2", + TCP_TIME_WAIT: "time_wait", + TCP_CLOSE: "close", + TCP_CLOSE_WAIT: "close_wait", + TCP_LAST_ACK: "last_ack", + TCP_LISTEN: "listen", + TCP_CLOSING: "closing", +} + +// SocketID holds the socket information of a request/response to the kernel +type SocketID struct { + SourcePort uint16 + DestinationPort uint16 + Source net.IP + Destination net.IP + Interface uint32 + Cookie [2]uint32 +} + +// Socket represents a netlink socket. +type Socket struct { + Family uint8 + State uint8 + Timer uint8 + Retrans uint8 + ID SocketID + Expires uint32 + RQueue uint32 + WQueue uint32 + UID uint32 + INode uint32 +} + +// SocketRequest holds the request/response of a connection to the kernel +type SocketRequest struct { + Family uint8 + Protocol uint8 + Ext uint8 + pad uint8 + States uint32 + ID SocketID +} + +type writeBuffer struct { + Bytes []byte + pos int +} + +func (b *writeBuffer) Write(c byte) { + b.Bytes[b.pos] = c + b.pos++ +} + +func (b *writeBuffer) Next(n int) []byte { + s := b.Bytes[b.pos : b.pos+n] + b.pos += n + return s +} + +// Serialize convert SocketRequest struct to bytes. +func (r *SocketRequest) Serialize() []byte { + b := writeBuffer{Bytes: make([]byte, sizeofSocketRequest)} + b.Write(r.Family) + b.Write(r.Protocol) + b.Write(r.Ext) + b.Write(r.pad) + native.PutUint32(b.Next(4), r.States) + networkOrder.PutUint16(b.Next(2), r.ID.SourcePort) + networkOrder.PutUint16(b.Next(2), r.ID.DestinationPort) + if r.Family == syscall.AF_INET6 { + copy(b.Next(16), r.ID.Source) + copy(b.Next(16), r.ID.Destination) + } else { + copy(b.Next(4), r.ID.Source.To4()) + b.Next(12) + copy(b.Next(4), r.ID.Destination.To4()) + b.Next(12) + } + native.PutUint32(b.Next(4), r.ID.Interface) + native.PutUint32(b.Next(4), r.ID.Cookie[0]) + native.PutUint32(b.Next(4), r.ID.Cookie[1]) + return b.Bytes +} + +// Len returns the size of a socket request +func (r *SocketRequest) Len() int { return sizeofSocketRequest } + +type readBuffer struct { + Bytes []byte + pos int +} + +func (b *readBuffer) Read() byte { + c := b.Bytes[b.pos] + b.pos++ + return c +} + +func (b *readBuffer) Next(n int) []byte { + s := b.Bytes[b.pos : b.pos+n] + b.pos += n + return s +} + +func (s *Socket) deserialize(b []byte) error { + if len(b) < sizeofSocket { + return fmt.Errorf("socket data short read (%d); want %d", len(b), sizeofSocket) + } + rb := readBuffer{Bytes: b} + s.Family = rb.Read() + s.State = rb.Read() + s.Timer = rb.Read() + s.Retrans = rb.Read() + s.ID.SourcePort = networkOrder.Uint16(rb.Next(2)) + s.ID.DestinationPort = networkOrder.Uint16(rb.Next(2)) + if s.Family == syscall.AF_INET6 { + s.ID.Source = net.IP(rb.Next(16)) + s.ID.Destination = net.IP(rb.Next(16)) + } else { + s.ID.Source = net.IPv4(rb.Read(), rb.Read(), rb.Read(), rb.Read()) + rb.Next(12) + s.ID.Destination = net.IPv4(rb.Read(), rb.Read(), rb.Read(), rb.Read()) + rb.Next(12) + } + s.ID.Interface = native.Uint32(rb.Next(4)) + s.ID.Cookie[0] = native.Uint32(rb.Next(4)) + s.ID.Cookie[1] = native.Uint32(rb.Next(4)) + s.Expires = native.Uint32(rb.Next(4)) + s.RQueue = native.Uint32(rb.Next(4)) + s.WQueue = native.Uint32(rb.Next(4)) + s.UID = native.Uint32(rb.Next(4)) + s.INode = native.Uint32(rb.Next(4)) + return nil +} + +// SocketKill kills a connection +func SocketKill(family, proto uint8, sockID SocketID) error { + + sockReq := &SocketRequest{ + Family: family, + Protocol: proto, + ID: sockID, + } + + req := nl.NewNetlinkRequest(SOCK_DESTROY, syscall.NLM_F_REQUEST|syscall.NLM_F_ACK) + req.AddData(sockReq) + _, err := req.Execute(syscall.NETLINK_INET_DIAG, 0) + if err != nil { + return err + } + return nil +} + +// SocketGet returns the list of active connections in the kernel +// filtered by several fields. Currently it returns connections +// filtered by source port and protocol. +func SocketGet(family uint8, proto uint8, srcPort, dstPort uint16, local, remote net.IP) ([]*Socket, error) { + _Id := SocketID{ + SourcePort: srcPort, + Cookie: [2]uint32{nl.TCPDIAG_NOCOOKIE, nl.TCPDIAG_NOCOOKIE}, + } + + sockReq := &SocketRequest{ + Family: family, + Protocol: proto, + States: TCP_ALL, + ID: _Id, + } + + return netlinkRequest(sockReq, family, proto, srcPort, dstPort, local, remote) +} + +// SocketsDump returns the list of all connections from the kernel +func SocketsDump(family uint8, proto uint8) ([]*Socket, error) { + sockReq := &SocketRequest{ + Family: family, + Protocol: proto, + States: TCP_ALL, + } + return netlinkRequest(sockReq, 0, 0, 0, 0, nil, nil) +} + +func netlinkRequest(sockReq *SocketRequest, family uint8, proto uint8, srcPort, dstPort uint16, local, remote net.IP) ([]*Socket, error) { + req := nl.NewNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, syscall.NLM_F_DUMP) + req.AddData(sockReq) + msgs, err := req.Execute(syscall.NETLINK_INET_DIAG, 0) + if err != nil { + return nil, err + } + if len(msgs) == 0 { + return nil, errors.New("Warning, no message nor error from netlink, or no connections found") + } + var sock []*Socket + for n, m := range msgs { + s := &Socket{} + if err = s.deserialize(m); err != nil { + log.Error("[%d] netlink socket error: %s, %d:%v -> %v:%d - %d:%v -> %v:%d", + n, TCPStatesMap[s.State], + srcPort, local, remote, dstPort, + s.ID.SourcePort, s.ID.Source, s.ID.Destination, s.ID.DestinationPort) + continue + } + if s.INode == 0 { + continue + } + + sock = append([]*Socket{s}, sock...) + } + return sock, err +} diff --git a/daemon/netlink/socket_test.go b/daemon/netlink/socket_test.go new file mode 100644 index 0000000..b37719b --- /dev/null +++ b/daemon/netlink/socket_test.go @@ -0,0 +1,116 @@ +package netlink + +import ( + "fmt" + "net" + "os" + "strconv" + "strings" + "testing" +) + +type Connection struct { + SrcIP net.IP + DstIP net.IP + Protocol string + SrcPort uint + DstPort uint + OutConn net.Conn + Listener net.Listener +} + +func EstablishConnection(proto, dst string) (net.Conn, error) { + c, err := net.Dial(proto, dst) + if err != nil { + fmt.Println(err) + return nil, err + } + return c, nil +} + +func ListenOnPort(proto, port string) (net.Listener, error) { + // TODO: UDP -> ListenUDP() or ListenPacket() + l, err := net.Listen(proto, port) + if err != nil { + fmt.Println(err) + return nil, err + } + return l, nil +} + +func setupConnection(proto string, connChan chan *Connection) { + listnr, _ := ListenOnPort(proto, "127.0.0.1:55555") + conn, err := EstablishConnection(proto, "127.0.0.1:55555") + if err != nil { + connChan <- nil + return + } + laddr := strings.Split(conn.LocalAddr().String(), ":") + daddr := strings.Split(conn.RemoteAddr().String(), ":") + sport, _ := strconv.Atoi(laddr[1]) + dport, _ := strconv.Atoi(daddr[1]) + + lconn := &Connection{ + SrcPort: uint(sport), + DstPort: uint(dport), + SrcIP: net.ParseIP(laddr[0]), + DstIP: net.ParseIP(daddr[0]), + Protocol: "tcp", + Listener: listnr, + OutConn: conn, + } + connChan <- lconn +} + +// TestNetlinkQueries tests queries to the kernel to get the inode of a connection. +// When using ProcFS as monitor method, we need that value to get the PID of an application. +// We also need it if for any reason auditd or ebpf doesn't return the PID of the application. +// TODO: test all the cases described in the GetSocketInfo() description. +func TestNetlinkTCPQueries(t *testing.T) { + // netlink tests disabled by default, they cause random failures on restricted + // environments. + if os.Getenv("NETLINK_TESTS") == "" { + t.Skip("Skipping netlink tests. Use NETLINK_TESTS=1 to launch these tests.") + } + + connChan := make(chan *Connection) + go setupConnection("tcp", connChan) + conn := <-connChan + if conn == nil { + t.Error("TestParseTCPConnection, conn nil") + } + + var inodes []int + uid := -1 + t.Run("Test GetSocketInfo", func(t *testing.T) { + uid, inodes = GetSocketInfo("tcp", conn.SrcIP, conn.SrcPort, conn.DstIP, conn.DstPort) + + if len(inodes) == 0 { + t.Error("inodes empty") + } + if uid != os.Getuid() { + t.Error("GetSocketInfo UID error:", uid, os.Getuid()) + } + }) + + t.Run("Test GetSocketInfoByInode", func(t *testing.T) { + socket, err := GetSocketInfoByInode(fmt.Sprint(inodes[0])) + if err != nil { + t.Error("GetSocketInfoByInode error:", err) + } + if socket == nil { + t.Error("GetSocketInfoByInode inode not found") + } + if socket.ID.SourcePort != uint16(conn.SrcPort) { + t.Error("GetSocketInfoByInode dstPort error:", socket) + } + if socket.ID.DestinationPort != uint16(conn.DstPort) { + t.Error("GetSocketInfoByInode dstPort error:", socket) + } + if socket.UID != uint32(os.Getuid()) { + t.Error("GetSocketInfoByInode UID error:", socket, os.Getuid()) + } + }) + + conn.Listener.Close() +} diff --git a/daemon/netstat/entry.go b/daemon/netstat/entry.go new file mode 100644 index 0000000..ebe433a --- /dev/null +++ b/daemon/netstat/entry.go @@ -0,0 +1,32 @@ +package netstat + +import ( + "net" +) + +// Entry holds the information of a /proc/net/* entry. +// For example, /proc/net/tcp: +// sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode +// 0: 0100007F:13AD 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 18083222 +type Entry struct { + Proto string + SrcIP net.IP + DstIP net.IP + UserId int + INode int + SrcPort uint + DstPort uint +} + +// NewEntry creates a new entry with values from /proc/net/ +func NewEntry(proto string, srcIP net.IP, srcPort uint, dstIP net.IP, dstPort uint, userId int, iNode int) Entry { + return Entry{ + Proto: proto, + SrcIP: srcIP, + SrcPort: srcPort, + DstIP: dstIP, + DstPort: dstPort, + UserId: userId, + INode: iNode, + } +} diff --git a/daemon/netstat/find.go b/daemon/netstat/find.go new file mode 100644 index 0000000..8560c65 --- /dev/null +++ b/daemon/netstat/find.go @@ -0,0 +1,51 @@ +package netstat + +import ( + "net" + "strings" + + "github.com/evilsocket/opensnitch/daemon/core" + "github.com/evilsocket/opensnitch/daemon/log" +) + +// FindEntry looks for the connection in the list of known connections in ProcFS. +func FindEntry(proto string, srcIP net.IP, srcPort uint, dstIP net.IP, dstPort uint) *Entry { + if entry := findEntryForProtocol(proto, srcIP, srcPort, dstIP, dstPort); entry != nil { + return entry + } + + ipv6Suffix := "6" + if core.IPv6Enabled && strings.HasSuffix(proto, ipv6Suffix) == false { + otherProto := proto + ipv6Suffix + log.Debug("Searching for %s netstat entry instead of %s", otherProto, proto) + if entry := findEntryForProtocol(otherProto, srcIP, srcPort, dstIP, dstPort); entry != nil { + return entry + } + } + + return &Entry{ + Proto: proto, + SrcIP: srcIP, + SrcPort: srcPort, + DstIP: dstIP, + DstPort: dstPort, + UserId: -1, + INode: -1, + } +} + +func findEntryForProtocol(proto string, srcIP net.IP, srcPort uint, dstIP net.IP, dstPort uint) *Entry { + entries, err := Parse(proto) + if err != nil { + log.Warning("Error while searching for %s netstat entry: %s", proto, err) + return nil + } + + for _, entry := range entries { + if srcIP.Equal(entry.SrcIP) && srcPort == entry.SrcPort && dstIP.Equal(entry.DstIP) && dstPort == entry.DstPort { + return &entry + } + } + + return nil +} diff --git a/daemon/netstat/parse.go b/daemon/netstat/parse.go new file mode 100644 index 0000000..3484119 --- /dev/null +++ b/daemon/netstat/parse.go @@ -0,0 +1,119 @@ +package netstat + +import ( + "bufio" + "encoding/binary" + "net" + "os" + "regexp" + "strconv" + + "github.com/evilsocket/opensnitch/daemon/core" + "github.com/evilsocket/opensnitch/daemon/log" +) + +var ( + parser = regexp.MustCompile(`(?i)` + + `\d+:\s+` + // sl + `([a-f0-9]{8,32}):([a-f0-9]{4})\s+` + // local_address + `([a-f0-9]{8,32}):([a-f0-9]{4})\s+` + // rem_address + `[a-f0-9]{2}\s+` + // st + `[a-f0-9]{8}:[a-f0-9]{8}\s+` + // tx_queue rx_queue + `[a-f0-9]{2}:[a-f0-9]{8}\s+` + // tr tm->when + `[a-f0-9]{8}\s+` + // retrnsmt + `(\d+)\s+` + // uid + `\d+\s+` + // timeout + `(\d+)\s+` + // inode + `.+`) // stuff we don't care about +) + +func decToInt(n string) int { + d, err := strconv.ParseInt(n, 10, 64) + if err != nil { + log.Fatal("Error while parsing %s to int: %s", n, err) + } + return int(d) +} + +func hexToInt(h string) uint { + d, err := strconv.ParseUint(h, 16, 64) + if err != nil { + log.Fatal("Error while parsing %s to int: %s", h, err) + } + return uint(d) +} + +func hexToInt2(h string) (uint, uint) { + if len(h) > 16 { + d, err := strconv.ParseUint(h[:16], 16, 64) + if err != nil { + log.Fatal("Error while parsing %s to int: %s", h[16:], err) + } + d2, err := strconv.ParseUint(h[16:], 16, 64) + if err != nil { + log.Fatal("Error while parsing %s to int: %s", h[16:], err) + } + return uint(d), uint(d2) + } + + d, err := strconv.ParseUint(h, 16, 64) + if err != nil { + log.Fatal("Error while parsing %s to int: %s", h[16:], err) + } + return uint(d), 0 +} + +func hexToIP(h string) net.IP { + n, m := hexToInt2(h) + var ip net.IP + if m != 0 { + ip = make(net.IP, 16) + // TODO: Check if this depends on machine endianness? + binary.LittleEndian.PutUint32(ip, uint32(n>>32)) + binary.LittleEndian.PutUint32(ip[4:], uint32(n)) + binary.LittleEndian.PutUint32(ip[8:], uint32(m>>32)) + binary.LittleEndian.PutUint32(ip[12:], uint32(m)) + } else { + ip = make(net.IP, 4) + binary.LittleEndian.PutUint32(ip, uint32(n)) + } + return ip +} + +// Parse scans and retrieves the opened connections, from /proc/net/ files +func Parse(proto string) ([]Entry, error) { + filename := core.ConcatStrings("/proc/net/", proto) + fd, err := os.Open(filename) + if err != nil { + return nil, err + } + defer fd.Close() + + entries := make([]Entry, 0) + scanner := bufio.NewScanner(fd) + for lineno := 0; scanner.Scan(); lineno++ { + // skip column names + if lineno == 0 { + continue + } + + line := core.Trim(scanner.Text()) + m := parser.FindStringSubmatch(line) + if m == nil { + log.Warning("Could not parse netstat line from %s: %s", filename, line) + continue + } + + entries = append(entries, NewEntry( + proto, + hexToIP(m[1]), + hexToInt(m[2]), + hexToIP(m[3]), + hexToInt(m[4]), + decToInt(m[5]), + decToInt(m[6]), + )) + } + + return entries, nil +} diff --git a/daemon/opensnitchd-dinit b/daemon/opensnitchd-dinit new file mode 100644 index 0000000..fdca2af --- /dev/null +++ b/daemon/opensnitchd-dinit @@ -0,0 +1,8 @@ +# Application firewall OpenSnitch +type = process +command = /usr/bin/opensnitchd -rules-path /etc/opensnitchd/rules +restart = true +smooth-recovery = yes +restart-delay = 15 +stop-timeout = 10 +restart-limit-count = 0 diff --git a/daemon/opensnitchd-openrc b/daemon/opensnitchd-openrc new file mode 100755 index 0000000..85a7329 --- /dev/null +++ b/daemon/opensnitchd-openrc @@ -0,0 +1,36 @@ +#!/sbin/openrc-run +# OpenSnitch firewall service + +depend() { + before net + after iptables ip6tables + use logger + provide firewall +} + +start_pre() { + /bin/mkdir -p /etc/opensnitchd/rules + /bin/chown -R root:root /etc/opensnitchd + /bin/chown root:root /var/log/opensnitchd.log + /bin/chmod -R 755 /etc/opensnitchd + /bin/chmod -R 0644 /etc/opensnitchd/rules + /bin/chmod 0600 /var/log/opensnitchd.log +} + +start() { + ebegin "Starting application firewall" + # only if the verbose flag is not set (rc-service opensnitchd start -v) + if [ -z "$VERBOSE" ]; then + # redirect stdout and stderr to /dev/null + /usr/local/bin/opensnitchd -rules-path /etc/opensnitchd/rules -log-file /var/log/opensnitchd.log > /dev/null 2>&1 & + else + /usr/local/bin/opensnitchd -rules-path /etc/opensnitchd/rules -log-file /var/log/opensnitchd.log + fi + eend $? +} + +stop() { + ebegin "Stopping application firewall" + /usr/bin/pkill -SIGINT opensnitchd + eend $? +} diff --git a/daemon/opensnitchd.service b/daemon/opensnitchd.service new file mode 100644 index 0000000..3f05fad --- /dev/null +++ b/daemon/opensnitchd.service @@ -0,0 +1,15 @@ +[Unit] +Description=Application firewall OpenSnitch +Documentation=https://github.com/evilsocket/opensnitch/wiki + +[Service] +Type=simple +PermissionsStartOnly=true +ExecStartPre=/bin/mkdir -p /etc/opensnitchd/rules +ExecStart=/usr/local/bin/opensnitchd -rules-path /etc/opensnitchd/rules +Restart=always +RestartSec=30 +TimeoutStopSec=10 + +[Install] +WantedBy=multi-user.target diff --git a/daemon/procmon/activepids.go b/daemon/procmon/activepids.go new file mode 100644 index 0000000..9b2009f --- /dev/null +++ b/daemon/procmon/activepids.go @@ -0,0 +1,90 @@ +package procmon + +import ( + "io/ioutil" + "strconv" + "strings" + "sync" + "time" + + "github.com/evilsocket/opensnitch/daemon/core" + "github.com/evilsocket/opensnitch/daemon/log" +) + +type value struct { + Process *Process + //Starttime uniquely identifies a process, it is the 22nd value in /proc/<PID>/stat + //if another process starts with the same PID, it's Starttime will be unique + Starttime uint64 +} + +var ( + activePids = make(map[uint64]value) + activePidsLock = sync.RWMutex{} +) + +//MonitorActivePids checks that each process in activePids +//is still running and if not running (or another process with the same pid is running), +//removes the pid from activePids +func MonitorActivePids() { + for { + time.Sleep(time.Second) + activePidsLock.Lock() + for k, v := range activePids { + data, err := ioutil.ReadFile(core.ConcatStrings("/proc/", strconv.FormatUint(k, 10), "/stat")) + if err != nil { + //file does not exists, pid has quit + delete(activePids, k) + pidsCache.delete(int(k)) + continue + } + startTime, err := strconv.ParseInt(strings.Split(string(data), " ")[21], 10, 64) + if err != nil { + log.Error("Could not find or convert Starttime. This should never happen. Please report this incident to the Opensnitch developers: %v", err) + delete(activePids, k) + pidsCache.delete(int(k)) + continue + } + if uint64(startTime) != v.Starttime { + //extremely unlikely: the original process has quit and another process + //was started with the same PID - all this in less than 1 second + log.Error("Same PID but different Starttime. Please report this incident to the Opensnitch developers.") + delete(activePids, k) + pidsCache.delete(int(k)) + continue + } + } + activePidsLock.Unlock() + } +} + +func findProcessInActivePidsCache(pid uint64) *Process { + activePidsLock.Lock() + defer activePidsLock.Unlock() + if value, ok := activePids[pid]; ok { + return value.Process + } + return nil +} + +// AddToActivePidsCache adds the given pid to a list of known processes. +func AddToActivePidsCache(pid uint64, proc *Process) { + + data, err := ioutil.ReadFile(core.ConcatStrings("/proc/", strconv.FormatUint(pid, 10), "/stat")) + if err != nil { + //most likely the process has quit by now + return + } + startTime, err2 := strconv.ParseInt(strings.Split(string(data), " ")[21], 10, 64) + if err2 != nil { + log.Error("Could not find or convert Starttime. This should never happen. Please report this incident to the Opensnitch developers: %v", err) + return + } + + activePidsLock.Lock() + activePids[pid] = value{ + Process: proc, + Starttime: uint64(startTime), + } + activePidsLock.Unlock() +} diff --git a/daemon/procmon/activepids_test.go b/daemon/procmon/activepids_test.go new file mode 100644 index 0000000..ccc2c44 --- /dev/null +++ b/daemon/procmon/activepids_test.go @@ -0,0 +1,104 @@ +package procmon + +import ( + "fmt" + "math/rand" + "os" + "os/exec" + "syscall" + "testing" + "time" +) + +//TestMonitorActivePids starts helper processes, adds them to activePids +//and then kills them and checks if monitorActivePids() removed the killed processes +//from activePids +func TestMonitorActivePids(t *testing.T) { + + if os.Getenv("helperBinaryMode") == "on" { + //we are in the "helper binary" mode, we were started with helperCmd.Start() (see below) + //do nothing, just wait to be killed + time.Sleep(time.Second * 10) + os.Exit(1) //will never get here; but keep it here just in case + } + + //we are in a normal "go test" mode + tmpDir := "/tmp/ostest_" + randString() + os.Mkdir(tmpDir, 0777) + fmt.Println("tmp dir", tmpDir) + defer os.RemoveAll(tmpDir) + + go MonitorActivePids() + + //build a "helper binary" with "go test -c -o /tmp/path" and put it into a tmp dir + helperBinaryPath := tmpDir + "/helper1" + goExecutable, _ := exec.LookPath("go") + cmd := exec.Command(goExecutable, "test", "-c", "-o", helperBinaryPath) + if err := cmd.Run(); err != nil { + t.Error("Error running go test -c", err) + } + + var numberOfHelpers = 5 + var helperProcs []*Process + //start helper binaries + for i := 0; i < numberOfHelpers; i++ { + var helperCmd *exec.Cmd + helperCmd = &exec.Cmd{ + Path: helperBinaryPath, + Args: []string{helperBinaryPath}, + Env: []string{"helperBinaryMode=on"}, + } + if err := helperCmd.Start(); err != nil { + t.Error("Error starting helper binary", err) + } + go func() { + helperCmd.Wait() //must Wait(), otherwise the helper process becomes a zombie when kill()ed + }() + + pid := helperCmd.Process.Pid + proc := NewProcess(pid, helperBinaryPath) + helperProcs = append(helperProcs, proc) + AddToActivePidsCache(uint64(pid), proc) + } + //sleep to make sure all processes started before we proceed + time.Sleep(time.Second * 1) + //make sure all PIDS are in the cache + for i := 0; i < numberOfHelpers; i++ { + proc := helperProcs[i] + pid := proc.ID + foundProc := findProcessInActivePidsCache(uint64(pid)) + if foundProc == nil { + t.Error("PID not found among active processes", pid) + } + if proc.Path != foundProc.Path || proc.ID != foundProc.ID { + t.Error("PID or path doesn't match with the found process") + } + } + //kill all helpers except for one + for i := 0; i < numberOfHelpers-1; i++ { + if err := syscall.Kill(helperProcs[i].ID, syscall.SIGTERM); err != nil { + t.Error("error in syscall.Kill", err) + } + } + //give the cache time to remove killed processes + time.Sleep(time.Second * 1) + + //make sure only the alive process is in the cache + foundProc := findProcessInActivePidsCache(uint64(helperProcs[numberOfHelpers-1].ID)) + if foundProc == nil { + t.Error("last alive PID is not found among active processes", foundProc) + } + if len(activePids) != 1 { + t.Error("more than 1 active PIDs left in cache") + } +} + +func randString() string { + rand.Seed(time.Now().UnixNano()) + var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + b := make([]rune, 10) + for i := range b { + b[i] = letterRunes[rand.Intn(len(letterRunes))] + } + return string(b) +} diff --git a/daemon/procmon/audit/client.go b/daemon/procmon/audit/client.go new file mode 100644 index 0000000..396cc06 --- /dev/null +++ b/daemon/procmon/audit/client.go @@ -0,0 +1,355 @@ +// Package audit reads auditd events from the builtin af_unix plugin, and parses +// the messages in order to proactively monitor pids which make connections. +// Once a connection is made and redirected to us via NFQUEUE, we +// lookup the connection inode in /proc, and add the corresponding PID with all +// the information of the process to a list of known PIDs. +// +// TODO: Prompt the user to allow/deny a connection/program as soon as it's +// started. +// +// Requisities: +// - install auditd and audispd-plugins +// - enable af_unix plugin /etc/audisp/plugins.d/af_unix.conf (active = yes) +// - auditctl -a always,exit -F arch=b64 -S socket,connect,execve -k opensnitchd +// - increase /etc/audisp/audispd.conf q_depth if there're dropped events +// - set write_logs to no if you don't need/want audit logs to be stored in the disk. +// +// read messages from the pipe to verify that it's working: +// socat unix-connect:/var/run/audispd_events stdio +// +// Audit event fields: +// https://github.com/linux-audit/audit-documentation/blob/master/specs/fields/field-dictionary.csv +// Record types: +// https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/Security_Guide/sec-Audit_Record_Types.html +// +// Documentation: +// https://github.com/linux-audit/audit-documentation +package audit + +import ( + "bufio" + "fmt" + "io" + "net" + "os" + "runtime" + "sort" + "sync" + "time" + + "github.com/evilsocket/opensnitch/daemon/core" + "github.com/evilsocket/opensnitch/daemon/log" +) + +// Event represents an audit event, which in our case can be an event of type +// socket, execve, socketpair or connect. +type Event struct { + Timestamp string // audit(xxxxxxx:nnnn) + Serial string + ProcName string // comm + ProcPath string // exe + ProcCmdLine string // proctitle + ProcDir string // cwd + ProcMode string // mode + TTY string + Pid int + UID int + Gid int + PPid int + EUid int + EGid int + OUid int + OGid int + UserName string // auid + DstHost net.IP + DstPort int + NetFamily string // inet, inet6, local + Success string + INode int + Dev string + Syscall int + Exit int + EventType string + RawEvent string + LastSeen time.Time +} + +// MaxEventAge is the maximum minutes an audit process can live without network activity. +const ( + MaxEventAge = int(10) +) + +var ( + // Lock holds a mutex + Lock sync.RWMutex + ourPid = os.Getpid() + // cache of events + events []*Event + eventsCleaner *time.Ticker + eventsCleanerChan = (chan bool)(nil) + // TODO: EventChan is an output channel where incoming auditd events will be written. + // If a client opens it. + EventChan = (chan Event)(nil) + eventsExitChan = (chan bool)(nil) + auditConn net.Conn + // TODO: we may need arm arch + rule64 = []string{"exit,always", "-F", "arch=b64", "-F", fmt.Sprint("ppid!=", ourPid), "-F", fmt.Sprint("pid!=", ourPid), "-S", "socket,connect", "-k", "opensnitch"} + rule32 = []string{"exit,always", "-F", "arch=b32", "-F", fmt.Sprint("ppid!=", ourPid), "-F", fmt.Sprint("pid!=", ourPid), "-S", "socketcall", "-F", "a0=1", "-k", "opensnitch"} + audispdPath = "/var/run/audispd_events" +) + +// OPENSNITCH_RULES_KEY is the mark we place on every event we are interested in. +const ( + OpensnitchRulesKey = "key=\"opensnitch\"" +) + +// GetEvents returns the list of processes which have opened a connection. +func GetEvents() []*Event { + return events +} + +// GetEventByPid returns an event given a pid. +func GetEventByPid(pid int) *Event { + Lock.RLock() + defer Lock.RUnlock() + + for _, event := range events { + if pid == event.Pid { + return event + } + } + + return nil +} + +// sortEvents sorts received events by time and elapsed time since latest network activity. +// newest PIDs will be placed on top of the list. +func sortEvents() { + sort.Slice(events, func(i, j int) bool { + now := time.Now() + elapsedTimeT := now.Sub(events[i].LastSeen) + elapsedTimeU := now.Sub(events[j].LastSeen) + t := events[i].LastSeen.UnixNano() + u := events[j].LastSeen.UnixNano() + return t > u && elapsedTimeT < elapsedTimeU + }) +} + +// cleanOldEvents deletes the PIDs which do not exist or that are too old to +// live. +// We start searching from the oldest to the newest. +// If the last network activity of a PID has been greater than MaxEventAge, +// then it'll be deleted. +func cleanOldEvents() { + Lock.Lock() + defer Lock.Unlock() + + for n := len(events) - 1; n >= 0; n-- { + now := time.Now() + elapsedTime := now.Sub(events[n].LastSeen) + if int(elapsedTime.Minutes()) >= MaxEventAge { + events = append(events[:n], events[n+1:]...) + continue + } + if core.Exists(fmt.Sprint("/proc/", events[n].Pid)) == false { + events = append(events[:n], events[n+1:]...) + } + } +} + +func deleteEvent(pid int) { + for n := range events { + if events[n].Pid == pid || events[n].PPid == pid { + deleteEventByIndex(n) + break + } + } +} + +func deleteEventByIndex(index int) { + Lock.Lock() + events = append(events[:index], events[index+1:]...) + Lock.Unlock() +} + +// AddEvent adds new event to the list of PIDs which have generate network +// activity. +// If the PID is already in the list, the LastSeen field is updated, to keep +// it alive. +func AddEvent(aevent *Event) { + if aevent == nil { + return + } + Lock.Lock() + defer Lock.Unlock() + + for n := 0; n < len(events); n++ { + if events[n].Pid == aevent.Pid && events[n].Syscall == aevent.Syscall { + if aevent.ProcCmdLine != "" || (aevent.ProcCmdLine == events[n].ProcCmdLine) { + events[n] = aevent + } + events[n].LastSeen = time.Now() + + sortEvents() + return + } + } + aevent.LastSeen = time.Now() + events = append([]*Event{aevent}, events...) +} + +// startEventsCleaner will review if the events in the cache need to be cleaned +// every 5 minutes. +func startEventsCleaner() { + for { + select { + case <-eventsCleanerChan: + goto Exit + case <-eventsCleaner.C: + cleanOldEvents() + } + } +Exit: + log.Debug("audit: cleanerRoutine stopped") +} + +func addRules() bool { + r64 := append([]string{"-A"}, rule64...) + r32 := append([]string{"-A"}, rule32...) + _, err64 := core.Exec("auditctl", r64) + _, err32 := core.Exec("auditctl", r32) + if err64 == nil && err32 == nil { + return true + } + log.Error("Error adding audit rule, err32=%v, err=%v", err32, err64) + return false +} + +func configureSyscalls() { + // XXX: what about a i386 process running on a x86_64 system? + if runtime.GOARCH == "386" { + syscallSOCKET = "1" + syscallCONNECT = "3" + syscallSOCKETPAIR = "8" + } +} + +func deleteRules() bool { + r64 := []string{"-D", "-k", "opensnitch"} + r32 := []string{"-D", "-k", "opensnitch"} + _, err64 := core.Exec("auditctl", r64) + _, err32 := core.Exec("auditctl", r32) + if err64 == nil && err32 == nil { + return true + } + log.Error("Error deleting audit rules, err32=%v, err64=%v", err32, err64) + return false +} + +func checkRules() bool { + // TODO + return true +} + +func checkStatus() bool { + // TODO + return true +} + +// Reader reads events from audisd af_unix pipe plugin. +// If the auditd daemon is stopped or restarted, the reader handle +// is closed, so we need to restablished the connection. +func Reader(r io.Reader, eventChan chan<- Event) { + if r == nil { + log.Error("Error reading auditd events. Is auditd running? is af_unix plugin enabled?") + return + } + reader := bufio.NewReader(r) + go startEventsCleaner() + + for { + select { + case <-eventsExitChan: + goto Exit + default: + buf, _, err := reader.ReadLine() + if err != nil { + if err == io.EOF { + log.Error("AuditReader: auditd stopped, reconnecting in 30s %s", err) + if newReader, err := reconnect(); err == nil { + reader = bufio.NewReader(newReader) + log.Important("Auditd reconnected, continue reading") + } + continue + } + log.Warning("AuditReader: auditd error %s", err) + break + } + + parseEvent(string(buf[0:len(buf)]), eventChan) + } + } +Exit: + log.Debug("audit.Reader() closed") +} + +// StartChannel creates a channel to receive events from Audit. +// Launch audit.Reader() in a goroutine: +// go audit.Reader(c, (chan<- audit.Event)(audit.EventChan)) +func StartChannel() { + EventChan = make(chan Event, 0) +} + +func reconnect() (net.Conn, error) { + deleteRules() + time.Sleep(30 * time.Second) + return connect() +} + +func connect() (net.Conn, error) { + addRules() + // TODO: make the unix socket path configurable + return net.Dial("unix", audispdPath) +} + +// Stop stops listening for events from auditd and delete the auditd rules. +func Stop() { + if auditConn != nil { + if err := auditConn.Close(); err != nil { + log.Warning("audit.Stop() error closing socket: %v", err) + } + } + + if eventsCleaner != nil { + eventsCleaner.Stop() + } + if eventsExitChan != nil { + eventsExitChan <- true + close(eventsExitChan) + } + if eventsCleanerChan != nil { + eventsCleanerChan <- true + close(eventsCleanerChan) + } + + deleteRules() + if EventChan != nil { + close(EventChan) + } +} + +// Start makes a new connection to the audisp af_unix socket. +func Start() (net.Conn, error) { + auditConn, err := connect() + if err != nil { + log.Error("auditd Start() connection error %v", err) + deleteRules() + return nil, err + } + + configureSyscalls() + eventsCleaner = time.NewTicker(time.Minute * 5) + eventsCleanerChan = make(chan bool) + eventsExitChan = make(chan bool) + return auditConn, err +} diff --git a/daemon/procmon/audit/parse.go b/daemon/procmon/audit/parse.go new file mode 100644 index 0000000..a666588 --- /dev/null +++ b/daemon/procmon/audit/parse.go @@ -0,0 +1,298 @@ +package audit + +import ( + "encoding/hex" + "fmt" + "net" + "regexp" + "strconv" + "strings" +) + +var ( + newEvent = false + netEvent = &Event{} + + // RegExp for parse audit messages + // https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/security_guide/sec-understanding_audit_log_files + auditRE, _ = regexp.Compile(`([a-zA-Z0-9\-_]+)=([a-zA-Z0-9:'\-\/\"\.\,_\(\)]+)`) + rawEvent = make(map[string]string) +) + +// amd64 syscalls definition +// if the platform is not amd64, it's redefined on Start() +var ( + syscallSOCKET = "41" + syscallCONNECT = "42" + syscallSOCKETPAIR = "53" + syscallEXECVE = "59" + syscallSOCKETCALL = "102" +) + +// /usr/include/x86_64-linux-gnu/bits/socket_type.h +const ( + sockSTREAM = "1" + sockDGRAM = "2" + sockRAW = "3" + sockSEQPACKET = "5" + sockPACKET = "10" + + // /usr/include/x86_64-linux-gnu/bits/socket.h + pfUNSPEC = "0" + pfLOCAL = "1" // PF_UNIX + pfINET = "2" + pfINET6 = "10" + + // /etc/protocols + protoIP = "0" + protoTCP = "6" + protoUDP = "17" +) + +// https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/Security_Guide/sec-Audit_Record_Types.html +const ( + AuditTypePROCTITLE = "type=PROCTITLE" + AuditTypeCWD = "type=CWD" + AuditTypePATH = "type=PATH" + AuditTypeEXECVE = "type=EXECVE" + AuditTypeSOCKADDR = "type=SOCKADDR" + AuditTypeSOCKETCALL = "type=SOCKETCALL" + AuditTypeEOE = "type=EOE" +) + +var ( + syscallSOCKETstr = fmt.Sprint("syscall=", syscallSOCKET) + syscallCONNECTstr = fmt.Sprint("syscall=", syscallCONNECT) + syscallSOCKETPAIRstr = fmt.Sprint("syscall=", syscallSOCKETPAIR) + syscallEXECVEstr = fmt.Sprint("syscall=", syscallEXECVE) + syscallSOCKETCALLstr = fmt.Sprint("syscall=", syscallSOCKETCALL) +) + +// parseNetLine parses a SOCKADDR message type of the form: +// saddr string: inet6 host:2001:4860:4860::8888 serv:53 +func parseNetLine(line string, decode bool) (family string, dstHost net.IP, dstPort int) { + + // 0:4 - type + // 4:8 - port + // 8:16 - ip + switch family := line[0:4]; family { + // local + // case "0100": + // ipv4 + case "0200": + octet2 := decodeString(line[4:8]) + octet := decodeString(line[8:16]) + host := fmt.Sprint(octet[0], ".", octet[1], ".", octet[2], ".", octet[3]) + fmt.Printf("dest ip: %s -- %s:%s\n", line[4:8], octet2, host) + // ipv6 + //case "0A00": + } + + if decode == true { + line = decodeString(line) + } + pieces := strings.Split(line, " ") + family = pieces[0] + + if family[:4] != "inet" { + return family, dstHost, 0 + } + + if len(pieces) > 1 && pieces[1][:5] == "host:" { + dstHost = net.ParseIP(strings.Split(pieces[1], "host:")[1]) + } + if len(pieces) > 2 && pieces[2][:5] == "serv:" { + _dstPort, err := strconv.Atoi(strings.Split(line, "serv:")[1]) + if err != nil { + dstPort = -1 + } else { + dstPort = _dstPort + } + } + + return family, dstHost, dstPort +} + +// decodeString will try to decode a string encoded in hexadecimal. +// If the string can not be decoded, the original string will be returned. +// In that case, usually it means that it's a non-encoded string. +func decodeString(s string) string { + decoded, err := hex.DecodeString(s) + if err != nil { + return s + } + return fmt.Sprintf("%s", decoded) +} + +// extractFields parsed an audit raw message, and extracts all the fields. +func extractFields(rawMessage string, newEvent *map[string]string) { + Lock.Lock() + defer Lock.Unlock() + + if auditRE == nil { + newEvent = nil + return + } + fieldList := auditRE.FindAllStringSubmatch(rawMessage, -1) + if fieldList == nil { + newEvent = nil + return + } + for _, field := range fieldList { + (*newEvent)[field[1]] = field[2] + } +} + +// populateEvent populates our Event from a raw parsed message. +func populateEvent(aevent *Event, eventFields *map[string]string) *Event { + if aevent == nil { + return nil + } + Lock.Lock() + defer Lock.Unlock() + + for k, v := range *eventFields { + switch k { + //case "a0": + //case "a1": + //case "a2": + case "fam": + if v == "local" { + return nil + } + aevent.NetFamily = v + case "lport": + aevent.DstPort, _ = strconv.Atoi(v) + // TODO + /*case "addr": + fmt.Println("addr: ", v) + case "daddr": + fmt.Println("daddr: ", v) + case "laddr": + aevent.DstHost = net.ParseIP(v) + case "saddr": + parseNetLine(v, true) + fmt.Println("saddr:", v) + */ + case "exe": + aevent.ProcPath = strings.Trim(decodeString(v), "\"") + case "comm": + aevent.ProcName = strings.Trim(decodeString(v), "\"") + // proctitle may be truncated to 128 characters, so don't rely on it, parse /proc/<pid>/instead + //case "proctitle": + // aevent.ProcCmdLine = strings.Trim(decodeString(v), "\"") + case "tty": + aevent.TTY = v + case "pid": + aevent.Pid, _ = strconv.Atoi(v) + case "ppid": + aevent.PPid, _ = strconv.Atoi(v) + case "uid": + aevent.UID, _ = strconv.Atoi(v) + case "gid": + aevent.Gid, _ = strconv.Atoi(v) + case "success": + aevent.Success = v + case "cwd": + aevent.ProcDir = strings.Trim(decodeString(v), "\"") + case "inode": + aevent.INode, _ = strconv.Atoi(v) + case "dev": + aevent.Dev = v + case "mode": + aevent.ProcMode = v + case "ouid": + aevent.OUid, _ = strconv.Atoi(v) + case "ogid": + aevent.OGid, _ = strconv.Atoi(v) + case "syscall": + aevent.Syscall, _ = strconv.Atoi(v) + case "exit": + aevent.Exit, _ = strconv.Atoi(v) + case "type": + aevent.EventType = v + case "msg": + parts := strings.Split(v[6:], ":") + aevent.Timestamp = parts[0] + aevent.Serial = parts[1][:len(parts[1])-1] + } + } + + return aevent +} + +// parseEvent parses an auditd event, discards the unwanted ones, and adds +// the ones we're interested in to an array. +// We're only interested in the socket,socketpair,connect and execve syscalls. +// Events from us are excluded. +// +// When we received an event, we parse and add it to the list as soon as we can. +// If the next messages of the set have additional information, we update the +// event. +func parseEvent(rawMessage string, eventChan chan<- Event) { + if newEvent == false && strings.Index(rawMessage, OpensnitchRulesKey) == -1 { + return + } + + aEvent := make(map[string]string) + if strings.Index(rawMessage, syscallSOCKETstr) != -1 || + strings.Index(rawMessage, syscallCONNECTstr) != -1 || + strings.Index(rawMessage, syscallSOCKETPAIRstr) != -1 || + strings.Index(rawMessage, syscallEXECVEstr) != -1 || + strings.Index(rawMessage, syscallSOCKETCALLstr) != -1 { + + extractFields(rawMessage, &aEvent) + if aEvent == nil { + return + } + newEvent = true + netEvent = &Event{} + netEvent = populateEvent(netEvent, &aEvent) + AddEvent(netEvent) + } else if newEvent == true && strings.Index(rawMessage, AuditTypePROCTITLE) != -1 { + extractFields(rawMessage, &aEvent) + if aEvent == nil { + return + } + netEvent = populateEvent(netEvent, &aEvent) + AddEvent(netEvent) + } else if newEvent == true && strings.Index(rawMessage, AuditTypeCWD) != -1 { + extractFields(rawMessage, &aEvent) + if aEvent == nil { + return + } + netEvent = populateEvent(netEvent, &aEvent) + AddEvent(netEvent) + } else if newEvent == true && strings.Index(rawMessage, AuditTypeEXECVE) != -1 { + extractFields(rawMessage, &aEvent) + if aEvent == nil { + return + } + netEvent = populateEvent(netEvent, &aEvent) + AddEvent(netEvent) + } else if newEvent == true && strings.Index(rawMessage, AuditTypePATH) != -1 { + extractFields(rawMessage, &aEvent) + if aEvent == nil { + return + } + netEvent = populateEvent(netEvent, &aEvent) + AddEvent(netEvent) + } else if newEvent == true && strings.Index(rawMessage, AuditTypeSOCKADDR) != -1 { + extractFields(rawMessage, &aEvent) + if aEvent == nil { + return + } + + netEvent = populateEvent(netEvent, &aEvent) + AddEvent(netEvent) + if EventChan != nil { + eventChan <- *netEvent + } + } else if newEvent == true && strings.Index(rawMessage, AuditTypeEOE) != -1 { + newEvent = false + AddEvent(netEvent) + if EventChan != nil { + eventChan <- *netEvent + } + } +} diff --git a/daemon/procmon/cache.go b/daemon/procmon/cache.go new file mode 100644 index 0000000..f284ff4 --- /dev/null +++ b/daemon/procmon/cache.go @@ -0,0 +1,339 @@ +package procmon + +import ( + "os" + "sort" + "strconv" + "sync" + "time" + + "github.com/evilsocket/opensnitch/daemon/core" +) + +// InodeItem represents an item of the InodesCache. +type InodeItem struct { + FdPath string + LastSeen int64 + Pid int + + sync.RWMutex +} + +// ProcItem represents an item of the pidsCache +type ProcItem struct { + FdPath string + Descriptors []string + LastSeen int64 + Pid int + + sync.RWMutex +} + +// CacheProcs holds the cache of processes that have established connections. +type CacheProcs struct { + items []*ProcItem + sync.RWMutex +} + +// CacheInodes holds the cache of Inodes. +// The key is formed as follow: +// inode+srcip+srcport+dstip+dstport +type CacheInodes struct { + items map[string]*InodeItem + sync.RWMutex +} + +var ( + // cache of inodes, which help to not iterate over all the pidsCache and + // descriptors of /proc/<pid>/fd/ + // 15-50us vs 50-80ms + // we hit this cache when: + // - we've blocked a connection and the process retries it several times until it gives up, + // - or when a process timeouts connecting to an IP/domain and it retries it again, + // - or when a process resolves a domain and then connects to the IP. + inodesCache = NewCacheOfInodes() + maxTTL = 3 // maximum 3 minutes of inactivity in cache. Really rare, usually they lasts less than a minute. + + // 2nd cache of already known running pids, which also saves time by + // iterating only over a few pids' descriptors, (30us-20ms vs. 50-80ms) + // since it's more likely that most of the connections will be made by the + // same (running) processes. + // The cache is ordered by time, placing in the first places those PIDs with + // active connections. + pidsCache CacheProcs + pidsDescriptorsCache = make(map[int][]string) + + cacheTicker = time.NewTicker(2 * time.Minute) +) + +// CacheCleanerTask checks periodically if the inodes in the cache must be removed. +func CacheCleanerTask() { + for { + select { + case <-cacheTicker.C: + inodesCache.cleanup() + } + } +} + +// NewCacheOfInodes returns a new cache for inodes. +func NewCacheOfInodes() *CacheInodes { + return &CacheInodes{ + items: make(map[string]*InodeItem), + } +} + +//****************************************************************************** +// items of the caches. + +func (i *InodeItem) updateTime() { + i.Lock() + i.LastSeen = time.Now().UnixNano() + i.Unlock() +} + +func (i *InodeItem) getTime() int64 { + i.RLock() + defer i.RUnlock() + return i.LastSeen +} + +func (p *ProcItem) updateTime() { + p.Lock() + p.LastSeen = time.Now().UnixNano() + p.Unlock() +} + +func (p *ProcItem) updateDescriptors(descriptors []string) { + p.Lock() + p.Descriptors = descriptors + p.Unlock() +} + +//****************************************************************************** +// cache of processes + +func (c *CacheProcs) add(fdPath string, fdList []string, pid int) { + c.Lock() + defer c.Unlock() + for n := range c.items { + item := c.items[n] + if item == nil { + continue + } + if item.Pid == pid { + item.updateTime() + return + } + } + + procItem := &ProcItem{ + Pid: pid, + FdPath: fdPath, + Descriptors: fdList, + LastSeen: time.Now().UnixNano(), + } + + c.setItems([]*ProcItem{procItem}, c.items) +} + +func (c *CacheProcs) sort(pid int) { + item := c.getItem(0) + if item != nil && item.Pid == pid { + return + } + c.RLock() + defer c.RUnlock() + + sort.Slice(c.items, func(i, j int) bool { + t := c.items[i].LastSeen + u := c.items[j].LastSeen + return t > u || t == u + }) +} + +func (c *CacheProcs) delete(pid int) { + c.Lock() + defer c.Unlock() + + for n, procItem := range c.items { + if procItem.Pid == pid { + c.deleteItem(n) + inodesCache.delete(pid) + break + } + } +} + +func (c *CacheProcs) deleteItem(pos int) { + nItems := len(c.items) + if pos < nItems { + c.setItems(c.items[:pos], c.items[pos+1:]) + } +} + +func (c *CacheProcs) setItems(newItems []*ProcItem, oldItems []*ProcItem) { + c.items = append(newItems, oldItems...) +} + +func (c *CacheProcs) getItem(index int) *ProcItem { + c.RLock() + defer c.RUnlock() + + if index >= len(c.items) { + return nil + } + + return c.items[index] +} + +func (c *CacheProcs) getItems() []*ProcItem { + return c.items +} + +func (c *CacheProcs) countItems() int { + c.RLock() + defer c.RUnlock() + + return len(c.items) +} + +// loop over the processes that have generated connections +func (c *CacheProcs) getPid(inode int, inodeKey string, expect string) (int, int) { + c.Lock() + defer c.Unlock() + + for n, procItem := range c.items { + if procItem == nil { + continue + } + + if idxDesc, _ := getPidDescriptorsFromCache(procItem.FdPath, inodeKey, expect, &procItem.Descriptors, procItem.Pid); idxDesc != -1 { + procItem.updateTime() + return procItem.Pid, n + } + + descriptors := lookupPidDescriptors(procItem.FdPath, procItem.Pid) + if descriptors == nil { + c.deleteItem(n) + continue + } + + procItem.updateDescriptors(descriptors) + if idxDesc, _ := getPidDescriptorsFromCache(procItem.FdPath, inodeKey, expect, &descriptors, procItem.Pid); idxDesc != -1 { + procItem.updateTime() + return procItem.Pid, n + } + } + + return -1, -1 +} + +//****************************************************************************** +// cache of inodes + +func (i *CacheInodes) add(key, descLink string, pid int) { + i.Lock() + defer i.Unlock() + + if descLink == "" { + descLink = core.ConcatStrings("/proc/", strconv.Itoa(pid), "/exe") + } + i.items[key] = &InodeItem{ + FdPath: descLink, + Pid: pid, + LastSeen: time.Now().UnixNano(), + } +} + +func (i *CacheInodes) delete(pid int) { + i.Lock() + defer i.Unlock() + + for k, inodeItem := range i.items { + if inodeItem.Pid == pid { + delete(i.items, k) + } + } +} + +func (i *CacheInodes) getPid(inodeKey string) int { + if item, ok := i.isInCache(inodeKey); ok { + // sometimes the process may have disappeared at this point + if _, err := os.Lstat(item.FdPath); err == nil { + item.updateTime() + return item.Pid + } + pidsCache.delete(item.Pid) + i.delItem(inodeKey) + } + + return -1 +} + +func (i *CacheInodes) delItem(inodeKey string) { + i.Lock() + defer i.Unlock() + delete(i.items, inodeKey) +} + +func (i *CacheInodes) getItem(inodeKey string) *InodeItem { + i.RLock() + defer i.RUnlock() + + return i.items[inodeKey] +} + +func (i *CacheInodes) getItems() map[string]*InodeItem { + i.RLock() + defer i.RUnlock() + + return i.items +} + +func (i *CacheInodes) isInCache(inodeKey string) (*InodeItem, bool) { + i.RLock() + defer i.RUnlock() + + if item, found := i.items[inodeKey]; found { + return item, true + } + return nil, false +} + +func (i *CacheInodes) cleanup() { + now := time.Now() + i.Lock() + defer i.Unlock() + for k := range i.items { + if i.items[k] == nil { + continue + } + lastSeen := now.Sub( + time.Unix(0, i.items[k].getTime()), + ) + if core.Exists(i.items[k].FdPath) == false || int(lastSeen.Minutes()) > maxTTL { + delete(i.items, k) + } + } +} + +func getPidDescriptorsFromCache(fdPath, inodeKey, expect string, descriptors *[]string, pid int) (int, *[]string) { + for fdIdx := 0; fdIdx < len(*descriptors); fdIdx++ { + descLink := core.ConcatStrings(fdPath, (*descriptors)[fdIdx]) + if link, err := os.Readlink(descLink); err == nil && link == expect { + if fdIdx > 0 { + // reordering helps to reduce look up times by a factor of 10. + fd := (*descriptors)[fdIdx] + *descriptors = append((*descriptors)[:fdIdx], (*descriptors)[fdIdx+1:]...) + *descriptors = append([]string{fd}, *descriptors...) + } + if _, ok := inodesCache.isInCache(inodeKey); ok { + inodesCache.add(inodeKey, descLink, pid) + } + return fdIdx, descriptors + } + } + + return -1, descriptors +} diff --git a/daemon/procmon/cache_test.go b/daemon/procmon/cache_test.go new file mode 100644 index 0000000..5a7cd17 --- /dev/null +++ b/daemon/procmon/cache_test.go @@ -0,0 +1,103 @@ +package procmon + +import ( + "fmt" + "testing" + "time" +) + +func TestCacheProcs(t *testing.T) { + fdList := []string{"0", "1", "2"} + pidsCache.add(fmt.Sprint("/proc/", myPid, "/fd/"), fdList, myPid) + t.Log("Pids in cache: ", pidsCache.countItems()) + + t.Run("Test addProcEntry", func(t *testing.T) { + if pidsCache.countItems() != 1 { + t.Error("pidsCache should be 1") + } + }) + + oldPid := pidsCache.getItem(0) + pidsCache.add(fmt.Sprint("/proc/", myPid, "/fd/"), fdList, myPid) + t.Run("Test addProcEntry update", func(t *testing.T) { + if pidsCache.countItems() != 1 { + t.Error("pidsCache should still be 1!", pidsCache) + } + oldTime := time.Unix(0, oldPid.LastSeen) + newTime := time.Unix(0, pidsCache.getItem(0).LastSeen) + if oldTime.Equal(newTime) == false { + t.Error("pidsCache, time not updated: ", oldTime, newTime) + } + }) + + pidsCache.add("/proc/2/fd", fdList, 2) + pidsCache.delete(2) + t.Run("Test deleteProcEntry", func(t *testing.T) { + if pidsCache.countItems() != 1 { + t.Error("pidsCache should be 1:", pidsCache.countItems()) + } + }) + + pid, _ := pidsCache.getPid(0, "", "/dev/null") + t.Run("Test getPidFromCache", func(t *testing.T) { + if pid != myPid { + t.Error("pid not found in cache", pidsCache.countItems()) + } + }) + + // should not crash, and the number of items should still be 1 + pidsCache.deleteItem(1) + t.Run("Test deleteItem check bounds", func(t *testing.T) { + if pidsCache.countItems() != 1 { + t.Error("deleteItem check bounds error", pidsCache.countItems()) + } + }) + + pidsCache.deleteItem(0) + t.Run("Test deleteItem", func(t *testing.T) { + if pidsCache.countItems() != 0 { + t.Error("deleteItem error", pidsCache.countItems()) + } + }) + t.Log("items in cache:", pidsCache.countItems()) + + // the key of an inodeCache entry is formed as: inodeNumer + srcIP + srcPort + dstIP + dstPort + inodeKey := "000000000127.0.0.144444127.0.0.153" + // add() expects a path to the inode fd (/proc/<pid>/fd/12345), but as getPid() will check the path in order to retrieve the pid, + // we just set it to "" and it'll use /proc/<pid>/exe + inodesCache.add(inodeKey, "", myPid) + t.Run("Test addInodeEntry", func(t *testing.T) { + if _, found := inodesCache.items[inodeKey]; !found { + t.Error("inodesCache, inode not added:", len(inodesCache.items), inodesCache.items) + } + }) + + pid = inodesCache.getPid(inodeKey) + t.Run("Test getPidByInodeFromCache", func(t *testing.T) { + if pid != myPid { + t.Error("inode not found in cache", pid, inodeKey, len(inodesCache.items), inodesCache.items) + } + }) + + // should delete all inodes of a pid + inodesCache.delete(myPid) + t.Run("Test deleteInodeEntry", func(t *testing.T) { + if _, found := inodesCache.items[inodeKey]; found { + t.Error("inodesCache, key found in cache but it should not exist", inodeKey, len(inodesCache.items), inodesCache.items) + } + }) +} + +// Test getPidDescriptorsFromCache descriptors (inodes) reordering. +// When an inode (descriptor) is found, if it's pushed to the top of the list, +// the next time we look for it will cost -10x. +// Without reordering, the inode 0 will always be found on the 10th position, +// taking an average of 100us instead of 30. +// Benchmark results with reordering: ~5600ns/op, without: ~56000ns/op. +func BenchmarkGetPid(b *testing.B) { + fdList := []string{"10", "9", "8", "7", "6", "5", "4", "3", "2", "1", "0"} + pidsCache.add(fmt.Sprint("/proc/", myPid, "/fd/"), fdList, myPid) + for i := 0; i < b.N; i++ { + pidsCache.getPid(0, "", "/dev/null") + } +} diff --git a/daemon/procmon/details.go b/daemon/procmon/details.go new file mode 100644 index 0000000..a0df194 --- /dev/null +++ b/daemon/procmon/details.go @@ -0,0 +1,313 @@ +package procmon + +import ( + "bufio" + "fmt" + "io/ioutil" + "os" + "regexp" + "strconv" + "strings" + + "github.com/evilsocket/opensnitch/daemon/core" + "github.com/evilsocket/opensnitch/daemon/dns" + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/evilsocket/opensnitch/daemon/netlink" +) + +var socketsRegex, _ = regexp.Compile(`socket:\[([0-9]+)\]`) + +// GetInfo collects information of a process. +func (p *Process) GetInfo() error { + if os.Getpid() == p.ID { + return nil + } + // if the PID dir doesn't exist, the process may have exited or be a kernel connection + // XXX: can a kernel connection exist without an entry in ProcFS? + if p.Path == "" && p.IsAlive() == false { + log.Debug("PID can't be read /proc/ %d %s", p.ID, p.Comm) + + // The Comm field shouldn't be empty if the proc monitor method is ebpf or audit. + // If it's proc and the corresponding entry doesn't exist, there's nothing we can + // do to inform the user about this process. + if p.Comm == "" { + return fmt.Errorf("Unable to get process information") + } + } + p.ReadCmdline() + p.ReadComm() + p.ReadCwd() + + if err := p.ReadPath(); err != nil { + log.Error("GetInfo() path can't be read") + return err + } + p.ReadEnv() + + return nil +} + +// GetExtraInfo collects information of a process. +func (p *Process) GetExtraInfo() error { + p.ReadEnv() + p.readDescriptors() + p.readIOStats() + p.readStatus() + + return nil +} + +// ReadComm reads the comm name from ProcFS /proc/<pid>/comm +func (p *Process) ReadComm() error { + if p.Comm != "" { + return nil + } + data, err := ioutil.ReadFile(core.ConcatStrings("/proc/", strconv.Itoa(p.ID), "/comm")) + if err != nil { + return err + } + p.Comm = core.Trim(string(data)) + return nil +} + +// ReadCwd reads the current working directory name from ProcFS /proc/<pid>/cwd +func (p *Process) ReadCwd() error { + if p.CWD != "" { + return nil + } + link, err := os.Readlink(core.ConcatStrings("/proc/", strconv.Itoa(p.ID), "/cwd")) + if err != nil { + return err + } + p.CWD = link + return nil +} + +// ReadEnv reads and parses the environment variables of a process. +func (p *Process) ReadEnv() { + data, err := ioutil.ReadFile(core.ConcatStrings("/proc/", strconv.Itoa(p.ID), "/environ")) + if err != nil { + return + } + for _, s := range strings.Split(string(data), "\x00") { + parts := strings.SplitN(core.Trim(s), "=", 2) + if parts != nil && len(parts) == 2 { + key := core.Trim(parts[0]) + val := core.Trim(parts[1]) + p.Env[key] = val + } + } +} + +// ReadPath reads the symbolic link that /proc/<pid>/exe points to. +// Note 1: this link might not exist on the root filesystem, it might +// have been executed from a container, so the real path would be: +// /proc/<pid>/root/<path that 'exe' points to> +// +// Note 2: +// There're at least 3 things that a (regular) kernel connection meets +// from userspace POV: +// - /proc/<pid>/cmdline and /proc/<pid>/maps empty +// - /proc/<pid>/exe can't be read +func (p *Process) ReadPath() error { + // avoid rereading the path + if p.Path != "" && core.IsAbsPath(p.Path) { + return nil + } + defer func() { + if p.Path == "" { + // determine if this process might be of a kernel task. + if data, err := ioutil.ReadFile(core.ConcatStrings("/proc/", strconv.Itoa(p.ID), "/maps")); err == nil && len(data) == 0 { + p.Path = "Kernel connection" + p.Args = append(p.Args, p.Comm) + return + } + p.Path = p.Comm + } + }() + + linkName := core.ConcatStrings("/proc/", strconv.Itoa(p.ID), "/exe") + if _, err := os.Lstat(linkName); err != nil { + return err + } + + // FIXME: this reading can give error: file name too long + link, err := os.Readlink(linkName) + if err != nil { + return err + } + p.SetPath(link) + return nil +} + +// SetPath sets the path of the process, and fixes it if it's needed. +func (p *Process) SetPath(path string) { + p.Path = path + p.CleanPath() +} + +// ReadCmdline reads the cmdline of the process from ProcFS /proc/<pid>/cmdline +// This file may be empty if the process is of a kernel task. +// It can also be empty for short-lived processes. +func (p *Process) ReadCmdline() { + if len(p.Args) > 0 { + return + } + if data, err := ioutil.ReadFile(core.ConcatStrings("/proc/", strconv.Itoa(p.ID), "/cmdline")); err == nil { + if len(data) == 0 { + return + } + for i, b := range data { + if b == 0x00 { + data[i] = byte(' ') + } + } + + args := strings.Split(string(data), " ") + for _, arg := range args { + arg = core.Trim(arg) + if arg != "" { + p.Args = append(p.Args, arg) + } + } + } + p.CleanArgs() +} + +// CleanArgs applies fixes on the cmdline arguments. +// - AppImages cmdline reports the execuable launched as /proc/self/exe, +// instead of the actual path to the binary. +func (p *Process) CleanArgs() { + if len(p.Args) > 0 && p.Args[0] == ProcSelf { + p.Args[0] = p.Path + } +} + +func (p *Process) readDescriptors() { + f, err := os.Open(core.ConcatStrings("/proc/", strconv.Itoa(p.ID), "/fd/")) + if err != nil { + return + } + fDesc, err := f.Readdir(-1) + f.Close() + p.Descriptors = nil + + for _, fd := range fDesc { + tempFd := &procDescriptors{ + Name: fd.Name(), + } + if link, err := os.Readlink(core.ConcatStrings("/proc/", strconv.Itoa(p.ID), "/fd/", fd.Name())); err == nil { + tempFd.SymLink = link + socket := socketsRegex.FindStringSubmatch(link) + if len(socket) > 0 { + socketInfo, err := netlink.GetSocketInfoByInode(socket[1]) + if err == nil { + tempFd.SymLink = fmt.Sprintf("socket:[%s] - %d:%s -> %s:%d, state: %s", fd.Name(), + socketInfo.ID.SourcePort, + socketInfo.ID.Source.String(), + dns.HostOr(socketInfo.ID.Destination, socketInfo.ID.Destination.String()), + socketInfo.ID.DestinationPort, + netlink.TCPStatesMap[socketInfo.State]) + } + } + + if linkInfo, err := os.Lstat(link); err == nil { + tempFd.Size = linkInfo.Size() + tempFd.ModTime = linkInfo.ModTime() + } + } + p.Descriptors = append(p.Descriptors, tempFd) + } +} + +func (p *Process) readIOStats() { + f, err := os.Open(core.ConcatStrings("/proc/", strconv.Itoa(p.ID), "/io")) + if err != nil { + return + } + defer f.Close() + + p.IOStats = &procIOstats{} + + scanner := bufio.NewScanner(f) + for scanner.Scan() { + s := strings.Split(scanner.Text(), " ") + switch s[0] { + case "rchar:": + p.IOStats.RChar, _ = strconv.ParseInt(s[1], 10, 64) + case "wchar:": + p.IOStats.WChar, _ = strconv.ParseInt(s[1], 10, 64) + case "syscr:": + p.IOStats.SyscallRead, _ = strconv.ParseInt(s[1], 10, 64) + case "syscw:": + p.IOStats.SyscallWrite, _ = strconv.ParseInt(s[1], 10, 64) + case "read_bytes:": + p.IOStats.ReadBytes, _ = strconv.ParseInt(s[1], 10, 64) + case "write_bytes:": + p.IOStats.WriteBytes, _ = strconv.ParseInt(s[1], 10, 64) + } + } +} + +func (p *Process) readStatus() { + if data, err := ioutil.ReadFile(core.ConcatStrings("/proc/", strconv.Itoa(p.ID), "/status")); err == nil { + p.Status = string(data) + } + if data, err := ioutil.ReadFile(core.ConcatStrings("/proc/", strconv.Itoa(p.ID), "/stat")); err == nil { + p.Stat = string(data) + } + if data, err := ioutil.ReadFile(core.ConcatStrings("/proc/", strconv.Itoa(p.ID), "/stack")); err == nil { + p.Stack = string(data) + } + if data, err := ioutil.ReadFile(core.ConcatStrings("/proc/", strconv.Itoa(p.ID), "/maps")); err == nil { + p.Maps = string(data) + } + if data, err := ioutil.ReadFile(core.ConcatStrings("/proc/", strconv.Itoa(p.ID), "/statm")); err == nil { + p.Statm = &procStatm{} + fmt.Sscanf(string(data), "%d %d %d %d %d %d %d", &p.Statm.Size, &p.Statm.Resident, &p.Statm.Shared, &p.Statm.Text, &p.Statm.Lib, &p.Statm.Data, &p.Statm.Dt) + } +} + +// CleanPath applies fixes on the path to the binary: +// - Remove extra characters from the link that it points to. +// When a running process is deleted, the symlink has the bytes " (deleted") +// appended to the link. +// - If the path is /proc/self/exe, resolve the symlink that it points to. +func (p *Process) CleanPath() { + + // Sometimes the path to the binary reported is the symbolic link of the process itself. + // This is not useful to the user, and besides it's a generic path that can represent + // to any process. + // Therefore we cannot use /proc/self/exe directly, because it resolves to our own process. + if strings.HasPrefix(p.Path, ProcSelf) { + if link, err := os.Readlink(core.ConcatStrings(ProcSelf, "/exe")); err == nil { + p.Path = link + return + } + + if len(p.Args) > 0 && p.Args[0] != "" { + p.Path = p.Args[0] + return + } + p.Path = p.Comm + } + + pathLen := len(p.Path) + if pathLen >= 10 && p.Path[pathLen-10:] == " (deleted)" { + p.Path = p.Path[:len(p.Path)-10] + } + + // We may receive relative paths from kernel, but the path of a process must be absolute + if core.IsAbsPath(p.Path) == false { + if err := p.ReadPath(); err != nil { + log.Debug("ClenPath() error reading process path%s", err) + return + } + } + +} + +// IsAlive checks if the process is still running +func (p *Process) IsAlive() bool { + return core.Exists(core.ConcatStrings("/proc/", strconv.Itoa(p.ID))) +} diff --git a/daemon/procmon/ebpf/cache.go b/daemon/procmon/ebpf/cache.go new file mode 100644 index 0000000..af0b78c --- /dev/null +++ b/daemon/procmon/ebpf/cache.go @@ -0,0 +1,195 @@ +package ebpf + +import ( + "sync" + "time" + + "github.com/evilsocket/opensnitch/daemon/procmon" +) + +// NewExecEvent constructs a new execEvent from the arguments. +func NewExecEvent(pid, ppid, uid uint32, path string, comm [16]byte) *execEvent { + ev := &execEvent{ + Type: EV_TYPE_EXEC, + PID: pid, + PPID: ppid, + UID: uid, + Comm: comm, + } + length := MaxPathLen + if len(path) < MaxPathLen { + length = len(path) + } + copy(ev.Filename[:], path[:length]) + return ev +} + +type execEventItem struct { + Proc procmon.Process + Event execEvent + LastSeen int64 +} + +type eventsStore struct { + execEvents map[uint32]*execEventItem + sync.RWMutex +} + +// NewEventsStore creates a new store of events. +func NewEventsStore() *eventsStore { + return &eventsStore{ + execEvents: make(map[uint32]*execEventItem), + } +} + +func (e *eventsStore) add(key uint32, event execEvent, proc procmon.Process) { + e.Lock() + defer e.Unlock() + e.execEvents[key] = &execEventItem{ + Proc: proc, + Event: event, + } +} + +func (e *eventsStore) isInStore(key uint32) (item *execEventItem, found bool) { + e.RLock() + defer e.RUnlock() + item, found = e.execEvents[key] + return +} + +func (e *eventsStore) delete(key uint32) { + e.Lock() + defer e.Unlock() + delete(e.execEvents, key) +} + +func (e *eventsStore) DeleteOldItems() { + e.Lock() + defer e.Unlock() + + for k, item := range e.execEvents { + if item.Proc.IsAlive() == false { + delete(e.execEvents, k) + } + } +} + +//----------------------------------------------------------------------------- + +type ebpfCacheItem struct { + Key []byte + Proc procmon.Process + LastSeen int64 +} + +type ebpfCacheType struct { + Items map[interface{}]*ebpfCacheItem + sync.RWMutex +} + +var ( + maxTTL = 40 // Seconds + maxCacheItems = 5000 + ebpfCache *ebpfCacheType + ebpfCacheTicker *time.Ticker +) + +// NewEbpfCacheItem creates a new cache item. +func NewEbpfCacheItem(key []byte, proc procmon.Process) *ebpfCacheItem { + return &ebpfCacheItem{ + Key: key, + Proc: proc, + LastSeen: time.Now().UnixNano(), + } +} + +func (i *ebpfCacheItem) isValid() bool { + lastSeen := time.Now().Sub( + time.Unix(0, i.LastSeen), + ) + return int(lastSeen.Seconds()) < maxTTL +} + +// NewEbpfCache creates a new cache store. +func NewEbpfCache() *ebpfCacheType { + ebpfCacheTicker = time.NewTicker(1 * time.Minute) + return &ebpfCacheType{ + Items: make(map[interface{}]*ebpfCacheItem, 0), + } +} + +func (e *ebpfCacheType) addNewItem(key interface{}, itemKey []byte, proc procmon.Process) { + e.Lock() + e.Items[key] = NewEbpfCacheItem(itemKey, proc) + e.Unlock() +} + +func (e *ebpfCacheType) isInCache(key interface{}) (item *ebpfCacheItem, found bool) { + leng := e.Len() + + e.Lock() + item, found = e.Items[key] + if found { + if item.isValid() { + e.update(key, item) + } else { + found = false + delete(e.Items, key) + } + } + e.Unlock() + + if leng > maxCacheItems { + e.DeleteOldItems() + } + return +} + +func (e *ebpfCacheType) update(key interface{}, item *ebpfCacheItem) { + item.LastSeen = time.Now().UnixNano() + e.Items[key] = item +} + +func (e *ebpfCacheType) Len() int { + e.RLock() + defer e.RUnlock() + return len(e.Items) +} + +func (e *ebpfCacheType) DeleteOldItems() { + length := e.Len() + + e.Lock() + defer e.Unlock() + + for k, item := range e.Items { + if length > maxCacheItems || (item != nil && !item.isValid()) { + delete(e.Items, k) + } + } +} + +func (e *ebpfCacheType) delete(key interface{}) { + e.Lock() + defer e.Unlock() + + if key, found := e.Items[key]; found { + delete(e.Items, key) + } +} + +func (e *ebpfCacheType) clear() { + if e == nil { + return + } + e.Lock() + defer e.Unlock() + for k := range e.Items { + delete(e.Items, k) + } + + if ebpfCacheTicker != nil { + ebpfCacheTicker.Stop() + } +} diff --git a/daemon/procmon/ebpf/debug.go b/daemon/procmon/ebpf/debug.go new file mode 100644 index 0000000..60717c1 --- /dev/null +++ b/daemon/procmon/ebpf/debug.go @@ -0,0 +1,101 @@ +package ebpf + +import ( + "fmt" + "os/exec" + "strconv" + "syscall" + "unsafe" + + "github.com/evilsocket/opensnitch/daemon/log" + daemonNetlink "github.com/evilsocket/opensnitch/daemon/netlink" + elf "github.com/iovisor/gobpf/elf" +) + +// print map contents. used only for debugging +func dumpMap(bpfmap *elf.Map, isIPv6 bool) { + var lookupKey []byte + var nextKey []byte + var value []byte + if !isIPv6 { + lookupKey = make([]byte, 12) + nextKey = make([]byte, 12) + } else { + lookupKey = make([]byte, 36) + nextKey = make([]byte, 36) + } + value = make([]byte, 40) + firstrun := true + i := 0 + for { + i++ + ok, err := m.LookupNextElement(bpfmap, unsafe.Pointer(&lookupKey[0]), + unsafe.Pointer(&nextKey[0]), unsafe.Pointer(&value[0])) + if err != nil { + log.Error("eBPF LookupNextElement error: %v", err) + return + } + if firstrun { + // on first run lookupKey is a dummy, nothing to delete + firstrun = false + copy(lookupKey, nextKey) + continue + } + fmt.Println("key, value", lookupKey, value) + + if !ok { //reached end of map + break + } + copy(lookupKey, nextKey) + } +} + +//PrintEverything prints all the stats. used only for debugging +func PrintEverything() { + bash, _ := exec.LookPath("bash") + //get the number of the first map + out, err := exec.Command(bash, "-c", "bpftool map show | head -n 1 | cut -d ':' -f1").Output() + if err != nil { + fmt.Println("bpftool map dump name tcpMap ", err) + } + i, _ := strconv.Atoi(string(out[:len(out)-1])) + fmt.Println("i is", i) + + //dump all maps for analysis + for j := i; j < i+14; j++ { + _, _ = exec.Command(bash, "-c", "bpftool map dump id "+strconv.Itoa(j)+" > dump"+strconv.Itoa(j)).Output() + } + + alreadyEstablished.RLock() + for sock1, v := range alreadyEstablished.TCP { + fmt.Println(*sock1, v) + } + + fmt.Println("---------------------") + for sock1, v := range alreadyEstablished.TCPv6 { + fmt.Println(*sock1, v) + } + alreadyEstablished.RUnlock() + + fmt.Println("---------------------") + sockets, _ := daemonNetlink.SocketsDump(syscall.AF_INET, syscall.IPPROTO_TCP) + for idx := range sockets { + fmt.Println("socket tcp: ", sockets[idx]) + } + fmt.Println("---------------------") + sockets, _ = daemonNetlink.SocketsDump(syscall.AF_INET6, syscall.IPPROTO_TCP) + for idx := range sockets { + fmt.Println("socket tcp6: ", sockets[idx]) + } + fmt.Println("---------------------") + sockets, _ = daemonNetlink.SocketsDump(syscall.AF_INET, syscall.IPPROTO_UDP) + for idx := range sockets { + fmt.Println("socket udp: ", sockets[idx]) + } + fmt.Println("---------------------") + sockets, _ = daemonNetlink.SocketsDump(syscall.AF_INET6, syscall.IPPROTO_UDP) + for idx := range sockets { + fmt.Println("socket udp6: ", sockets[idx]) + } + +} diff --git a/daemon/procmon/ebpf/ebpf.go b/daemon/procmon/ebpf/ebpf.go new file mode 100644 index 0000000..a3e6ded --- /dev/null +++ b/daemon/procmon/ebpf/ebpf.go @@ -0,0 +1,246 @@ +package ebpf + +import ( + "context" + "encoding/binary" + "fmt" + "sync" + "syscall" + "unsafe" + + "github.com/evilsocket/opensnitch/daemon/core" + "github.com/evilsocket/opensnitch/daemon/log" + daemonNetlink "github.com/evilsocket/opensnitch/daemon/netlink" + "github.com/evilsocket/opensnitch/daemon/procmon" + elf "github.com/iovisor/gobpf/elf" + "github.com/vishvananda/netlink" +) + +// contains pointers to ebpf maps for a given protocol (tcp/udp/v6) +type ebpfMapsForProto struct { + bpfmap *elf.Map +} + +//Not in use, ~4usec faster lookup compared to m.LookupElement() + +// mimics union bpf_attr's anonymous struct used by BPF_MAP_*_ELEM commands +// from <linux_headers>/include/uapi/linux/bpf.h +type bpf_lookup_elem_t struct { + map_fd uint64 //even though in bpf.h its type is __u32, we must make it 8 bytes long + //because "key" is of type __aligned_u64, i.e. "key" must be aligned on an 8-byte boundary + key uintptr + value uintptr +} + +type alreadyEstablishedConns struct { + TCP map[*daemonNetlink.Socket]int + TCPv6 map[*daemonNetlink.Socket]int + sync.RWMutex +} + +// list of returned errors +const ( + NoError = iota + NotAvailable + EventsNotAvailable +) + +// Error returns the error type and a message with the explanation +type Error struct { + Msg error + What int +} + +var ( + m, perfMod *elf.Module + lock = sync.RWMutex{} + mapSize = uint(12000) + ebpfMaps map[string]*ebpfMapsForProto + modulesPath string + + //connections which were established at the time when opensnitch started + alreadyEstablished = alreadyEstablishedConns{ + TCP: make(map[*daemonNetlink.Socket]int), + TCPv6: make(map[*daemonNetlink.Socket]int), + } + ctxTasks context.Context + cancelTasks context.CancelFunc + running = false + + maxKernelEvents = 32768 + kernelEvents = make(chan interface{}, maxKernelEvents) + + // list of local addresses of this machine + localAddresses = make(map[string]netlink.Addr) + + hostByteOrder binary.ByteOrder +) + +// Start installs ebpf kprobes +func Start(modPath string) *Error { + modulesPath = modPath + + setRunning(false) + if err := mountDebugFS(); err != nil { + log.Error("ebpf.Start -> mount debugfs error. Report on github please: %s", err) + return &Error{ + fmt.Errorf("ebpf.Start: mount debugfs error. Report on github please: %s", err), + NotAvailable, + } + + } + var err error + m, err = core.LoadEbpfModule("opensnitch.o", modulesPath) + if err != nil { + log.Error("%s", err) + dispatchErrorEvent(fmt.Sprint("[eBPF]: ", err.Error())) + return &Error{ + fmt.Errorf("[eBPF] Error loading opensnitch.o: %s", err.Error()), + NotAvailable, + } + } + m.EnableOptionCompatProbe() + + // if previous shutdown was unclean, then we must remove the dangling kprobe + // and install it again (close the module and load it again) + + if err := m.EnableKprobes(0); err != nil { + m.Close() + if err := m.Load(nil); err != nil { + return &Error{ + fmt.Errorf("eBPF failed to load /etc/opensnitchd/opensnitch.o (2): %v", err), + NotAvailable, + } + } + if err := m.EnableKprobes(0); err != nil { + return &Error{ + fmt.Errorf("eBPF error when enabling kprobes: %v", err), + NotAvailable, + } + } + } + determineHostByteOrder() + + ebpfMaps = map[string]*ebpfMapsForProto{ + "tcp": { + bpfmap: m.Map("tcpMap")}, + "tcp6": { + bpfmap: m.Map("tcpv6Map")}, + "udp": { + bpfmap: m.Map("udpMap")}, + "udp6": { + bpfmap: m.Map("udpv6Map")}, + } + for prot, mfp := range ebpfMaps { + if mfp.bpfmap == nil { + return &Error{ + fmt.Errorf("eBPF module opensnitch.o malformed, bpfmap[%s] nil", prot), + NotAvailable, + } + } + } + + ctxTasks, cancelTasks = context.WithCancel(context.Background()) + ebpfCache = NewEbpfCache() + initEventsStreamer() + + saveEstablishedConnections(uint8(syscall.AF_INET)) + if core.IPv6Enabled { + saveEstablishedConnections(uint8(syscall.AF_INET6)) + } + + go monitorCache() + go monitorMaps() + go monitorLocalAddresses() + go monitorAlreadyEstablished() + + setRunning(true) + return nil +} + +func saveEstablishedConnections(commDomain uint8) error { + // save already established connections + socketListTCP, err := daemonNetlink.SocketsDump(commDomain, uint8(syscall.IPPROTO_TCP)) + if err != nil { + log.Debug("eBPF could not dump TCP (%d) sockets via netlink: %v", commDomain, err) + return err + } + + for _, sock := range socketListTCP { + inode := int((*sock).INode) + pid := procmon.GetPIDFromINode(inode, fmt.Sprint(inode, + (*sock).ID.Source, (*sock).ID.SourcePort, (*sock).ID.Destination, (*sock).ID.DestinationPort)) + alreadyEstablished.Lock() + alreadyEstablished.TCP[sock] = pid + alreadyEstablished.Unlock() + } + return nil +} + +func setRunning(status bool) { + lock.Lock() + defer lock.Unlock() + + running = status +} + +// Stop stops monitoring connections using kprobes +func Stop() { + lock.RLock() + defer lock.RUnlock() + if running == false { + return + } + cancelTasks() + ebpfCache.clear() + + if m != nil { + m.Close() + } + + for pm := range perfMapList { + if pm != nil { + pm.PollStop() + } + } + for k, mod := range perfMapList { + if mod != nil { + mod.Close() + delete(perfMapList, k) + } + } + if perfMod != nil { + perfMod.Close() + } +} + +// make bpf() syscall with bpf_lookup prepared by the caller +func makeBpfSyscall(bpf_lookup *bpf_lookup_elem_t) uintptr { + BPF_MAP_LOOKUP_ELEM := 1 //cmd number + syscall_BPF := 321 //syscall number + sizeOfStruct := 40 //sizeof bpf_lookup_elem_t struct + + r1, _, _ := syscall.Syscall(uintptr(syscall_BPF), uintptr(BPF_MAP_LOOKUP_ELEM), + uintptr(unsafe.Pointer(bpf_lookup)), uintptr(sizeOfStruct)) + return r1 +} + +func dispatchErrorEvent(what string) { + log.Error(what) + dispatchEvent(what) +} + +func dispatchEvent(data interface{}) { + if len(kernelEvents) > maxKernelEvents-1 { + fmt.Printf("kernelEvents queue full (%d)", len(kernelEvents)) + <-kernelEvents + } + select { + case kernelEvents <- data: + default: + } +} + +func Events() <-chan interface{} { + return kernelEvents +} diff --git a/daemon/procmon/ebpf/events.go b/daemon/procmon/ebpf/events.go new file mode 100644 index 0000000..a76beba --- /dev/null +++ b/daemon/procmon/ebpf/events.go @@ -0,0 +1,235 @@ +package ebpf + +import ( + "bytes" + "encoding/binary" + "fmt" + "os" + "os/signal" + + "github.com/evilsocket/opensnitch/daemon/core" + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/evilsocket/opensnitch/daemon/procmon" + elf "github.com/iovisor/gobpf/elf" +) + +// MaxPathLen defines the maximum length of a path, as defined by the kernel: +// https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/limits.h#L13 +const MaxPathLen = 4096 + +// MaxArgs defines the maximum number of arguments allowed +const MaxArgs = 20 + +// MaxArgLen defines the maximum length of each argument. +// NOTE: this value is 131072 (PAGE_SIZE * 32) +// https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/binfmts.h#L16 +const MaxArgLen = 256 + +// TaskCommLen is the maximum num of characters of the comm field +const TaskCommLen = 16 + +type execEvent struct { + Type uint64 + PID uint32 + UID uint32 + PPID uint32 + RetCode uint32 + ArgsCount uint8 + ArgsPartial uint8 + Filename [MaxPathLen]byte + Args [MaxArgs][MaxArgLen]byte + Comm [TaskCommLen]byte + Pad1 uint16 + Pad2 uint32 +} + +// Struct that holds the metadata of a connection. +// When we receive a new connection, we look for it on the eBPF maps, +// and if it's found, this information is returned. +type networkEventT struct { + Pid uint64 + UID uint64 + Comm [TaskCommLen]byte +} + +// List of supported events +const ( + EV_TYPE_NONE = iota + EV_TYPE_EXEC + EV_TYPE_EXECVEAT + EV_TYPE_FORK + EV_TYPE_SCHED_EXIT +) + +var ( + execEvents = NewEventsStore() + perfMapList = make(map[*elf.PerfMap]*elf.Module) + // total workers spawned by the different events PerfMaps + eventWorkers = 0 + perfMapName = "proc-events" + + // default value is 8. + // Not enough to handle high loads such http downloads, torent traffic, etc. + // (regular desktop usage) + ringBuffSize = 64 // * PAGE_SIZE (4k usually) +) + +func initEventsStreamer() { + elfOpts := make(map[string]elf.SectionParams) + elfOpts["maps/"+perfMapName] = elf.SectionParams{PerfRingBufferPageCount: ringBuffSize} + var err error + perfMod, err = core.LoadEbpfModule("opensnitch-procs.o", modulesPath) + if err != nil { + dispatchErrorEvent(fmt.Sprint("[eBPF events]: ", err)) + return + } + perfMod.EnableOptionCompatProbe() + + if err = perfMod.Load(elfOpts); err != nil { + dispatchErrorEvent(fmt.Sprint("[eBPF events]: ", err)) + return + } + + tracepoints := []string{ + "tracepoint/sched/sched_process_exit", + "tracepoint/syscalls/sys_enter_execve", + "tracepoint/syscalls/sys_enter_execveat", + "tracepoint/syscalls/sys_exit_execve", + "tracepoint/syscalls/sys_exit_execveat", + //"tracepoint/sched/sched_process_exec", + //"tracepoint/sched/sched_process_fork", + } + + // Enable tracepoints first, that way if kprobes fail loading we'll still have some + for _, tp := range tracepoints { + err = perfMod.EnableTracepoint(tp) + if err != nil { + dispatchErrorEvent(fmt.Sprintf("[eBPF events] error enabling tracepoint %s: %s", tp, err)) + } + } + + if err = perfMod.EnableKprobes(0); err != nil { + // if previous shutdown was unclean, then we must remove the dangling kprobe + // and install it again (close the module and load it again) + perfMod.Close() + if err = perfMod.Load(elfOpts); err != nil { + dispatchErrorEvent(fmt.Sprintf("[eBPF events] failed to load /etc/opensnitchd/opensnitch-procs.o (2): %v", err)) + return + } + if err = perfMod.EnableKprobes(0); err != nil { + dispatchErrorEvent(fmt.Sprintf("[eBPF events] error enabling kprobes: %v", err)) + } + } + + sig := make(chan os.Signal, 1) + signal.Notify(sig, os.Interrupt, os.Kill) + go func(sig chan os.Signal) { + <-sig + }(sig) + + eventWorkers = 0 + initPerfMap(perfMod) +} + +func initPerfMap(mod *elf.Module) { + perfChan := make(chan []byte) + lostEvents := make(chan uint64, 1) + var err error + perfMap, err := elf.InitPerfMap(mod, perfMapName, perfChan, lostEvents) + if err != nil { + dispatchErrorEvent(fmt.Sprintf("[eBPF events] Error initializing eBPF events perfMap: %s", err)) + return + } + perfMapList[perfMap] = mod + + eventWorkers += 4 + for i := 0; i < eventWorkers; i++ { + go streamEventsWorker(i, perfChan, lostEvents, kernelEvents, execEvents) + } + perfMap.PollStart() +} + +func streamEventsWorker(id int, chn chan []byte, lost chan uint64, kernelEvents chan interface{}, execEvents *eventsStore) { + var event execEvent + errors := 0 + maxErrors := 20 // we should have no errors. + tooManyErrors := func() bool { + errors++ + if errors > maxErrors { + log.Error("[eBPF events] too many errors parsing events from kernel") + log.Error("verify that you're using the correct eBPF modules for this version (%s)", core.Version) + return true + } + return false + } + + for { + select { + case <-ctxTasks.Done(): + goto Exit + case l := <-lost: + log.Debug("Lost ebpf events: %d", l) + case d := <-chn: + if err := binary.Read(bytes.NewBuffer(d), hostByteOrder, &event); err != nil { + log.Debug("[eBPF events #%d] error: %s", id, err) + if tooManyErrors() { + goto Exit + } + + } else { + switch event.Type { + case EV_TYPE_EXEC, EV_TYPE_EXECVEAT: + if _, found := execEvents.isInStore(event.PID); found { + log.Debug("[eBPF event inCache] -> %d", event.PID) + continue + } + proc := event2process(&event) + if proc == nil { + continue + } + execEvents.add(event.PID, event, *proc) + + case EV_TYPE_SCHED_EXIT: + log.Debug("[eBPF exit event] -> %d", event.PID) + if _, found := execEvents.isInStore(event.PID); found { + log.Debug("[eBPF exit event inCache] -> %d", event.PID) + execEvents.delete(event.PID) + } + } + } + } + } + +Exit: + log.Debug("perfMap goroutine exited #%d", id) +} + +func event2process(event *execEvent) (proc *procmon.Process) { + + proc = procmon.NewProcess(int(event.PID), byteArrayToString(event.Comm[:])) + // trust process path received from kernel + path := byteArrayToString(event.Filename[:]) + if path != "" { + proc.SetPath(path) + } else { + if proc.ReadPath() != nil { + return nil + } + } + proc.ReadCwd() + proc.ReadEnv() + proc.UID = int(event.UID) + proc.PPID = int(event.PPID) + + if event.ArgsPartial == 0 { + for i := 0; i < int(event.ArgsCount); i++ { + proc.Args = append(proc.Args, byteArrayToString(event.Args[i][:])) + } + proc.CleanArgs() + } else { + proc.ReadCmdline() + } + log.Debug("[eBPF exec event] ppid: %d, pid: %d, %s -> %s", event.PPID, event.PID, proc.Path, proc.Args) + + return +} diff --git a/daemon/procmon/ebpf/find.go b/daemon/procmon/ebpf/find.go new file mode 100644 index 0000000..1c53646 --- /dev/null +++ b/daemon/procmon/ebpf/find.go @@ -0,0 +1,232 @@ +package ebpf + +import ( + "encoding/binary" + "fmt" + "net" + "strconv" + "unsafe" + + "github.com/evilsocket/opensnitch/daemon/core" + "github.com/evilsocket/opensnitch/daemon/log" + daemonNetlink "github.com/evilsocket/opensnitch/daemon/netlink" + "github.com/evilsocket/opensnitch/daemon/procmon" +) + +// we need to manually remove old connections from a bpf map + +// GetPid looks up process pid in a bpf map. +// If it's not found, it searches already-established TCP connections. +// Returns the process if found. +// Additionally, if the process has been found by swapping fields, it'll return +// a flag indicating it. +func GetPid(proto string, srcPort uint, srcIP net.IP, dstIP net.IP, dstPort uint) (*procmon.Process, bool, error) { + if proc := getPidFromEbpf(proto, srcPort, srcIP, dstIP, dstPort); proc != nil { + return proc, false, nil + } + if findAddressInLocalAddresses(dstIP) { + // FIXME: systemd-resolved sometimes makes a TCP Fast Open connection to a DNS server (8.8.8.8 on my machine) + // and we get a packet here with **source** (not detination!!!) IP 8.8.8.8 + // Maybe it's an in-kernel response with spoofed IP because resolved's TCP Fast Open packet, nor the response. + // Another scenario when systemd-resolved or dnscrypt-proxy is used, is that every outbound connection has + // the fields swapped: + // 443:public-ip -> local-ip:local-port , like if it was a response (but it's not). + // Swapping connection fields helps to identify the connection + pid + process, and continue working as usual + // when systemd-resolved is being used. But we should understand why is this happenning. + + if proc := getPidFromEbpf(proto, dstPort, dstIP, srcIP, srcPort); proc != nil { + return proc, true, fmt.Errorf("[ebpf conn] FIXME: found swapping fields, systemd-resolved is that you? set DNS=x.x.x.x to your DNS server in /etc/systemd/resolved.conf to workaround this problem") + } + return nil, false, fmt.Errorf("[ebpf conn] unknown source IP: %s", srcIP) + } + //check if it comes from already established TCP + if proto == "tcp" || proto == "tcp6" { + if pid, uid, err := findInAlreadyEstablishedTCP(proto, srcPort, srcIP, dstIP, dstPort); err == nil { + proc := procmon.NewProcess(pid, "") + proc.GetInfo() + proc.UID = uid + return proc, false, nil + } + } + + //using netlink.GetSocketInfo to check if UID is 0 (in-kernel connection) + if uid, _ := daemonNetlink.GetSocketInfo(proto, srcIP, srcPort, dstIP, dstPort); uid == 0 { + return nil, false, nil + } + return nil, false, nil +} + +// getPidFromEbpf looks up a connection in bpf map and returns PID if found +// the lookup keys and values are defined in opensnitch.c , e.g. +// +// struct tcp_key_t { +// u16 sport; +// u32 daddr; +// u16 dport; +// u32 saddr; +// }__attribute__((packed)); + +// struct tcp_value_t{ +// u64 pid; +// u64 uid; +// u64 counter; +// char[TASK_COMM_LEN] comm; // 16 bytes +// }__attribute__((packed)); + +func getPidFromEbpf(proto string, srcPort uint, srcIP net.IP, dstIP net.IP, dstPort uint) (proc *procmon.Process) { + // Some connections, like broadcasts, are only seen in eBPF once, + // but some applications send 1 connection per network interface. + // If we delete the eBPF entry the first time we see it, we won't find + // the connection the next times. + delItemIfFound := true + + _, ok := ebpfMaps[proto] + if !ok { + return + } + + var value networkEventT + var key []byte + var isIP4 bool = (proto == "tcp") || (proto == "udp") || (proto == "udplite") + + if isIP4 { + key = make([]byte, 12) + copy(key[2:6], dstIP) + binary.BigEndian.PutUint16(key[6:8], uint16(dstPort)) + copy(key[8:12], srcIP) + } else { // IPv6 + key = make([]byte, 36) + copy(key[2:18], dstIP) + binary.BigEndian.PutUint16(key[18:20], uint16(dstPort)) + copy(key[20:36], srcIP) + } + hostByteOrder.PutUint16(key[0:2], uint16(srcPort)) + + k := core.ConcatStrings( + proto, + strconv.FormatUint(uint64(srcPort), 10), + srcIP.String(), + dstIP.String(), + strconv.FormatUint(uint64(dstPort), 10)) + if cacheItem, isInCache := ebpfCache.isInCache(k); isInCache { + // should we re-read the info? + // environ vars might have changed + //proc.GetInfo() + deleteEbpfEntry(proto, unsafe.Pointer(&key[0])) + proc = &cacheItem.Proc + log.Debug("[ebpf conn] in cache: %s, %d -> %s", k, proc.ID, proc.Path) + return + } + + err := m.LookupElement(ebpfMaps[proto].bpfmap, unsafe.Pointer(&key[0]), unsafe.Pointer(&value)) + if err != nil { + // key not found + // sometimes srcIP is 0.0.0.0. Happens especially with UDP sendto() + // for example: 57621:10.0.3.1 -> 10.0.3.255:57621 , reported as: 0.0.0.0 -> 10.0.3.255 + if isIP4 { + zeroes := make([]byte, 4) + copy(key[8:12], zeroes) + } else { + zeroes := make([]byte, 16) + copy(key[20:36], zeroes) + } + err = m.LookupElement(ebpfMaps[proto].bpfmap, unsafe.Pointer(&key[0]), unsafe.Pointer(&value)) + if err == nil { + delItemIfFound = false + } + } + if err != nil && proto == "udp" && srcIP.String() == dstIP.String() { + // very rarely I see this connection. It has srcIP and dstIP == 0.0.0.0 in ebpf map + // it is a localhost to localhost connection + // srcIP was already set to 0, set dstIP to zero also + // TODO try to reproduce it and look for srcIP/dstIP in other kernel structures + zeroes := make([]byte, 4) + copy(key[2:6], zeroes) + err = m.LookupElement(ebpfMaps[proto].bpfmap, unsafe.Pointer(&key[0]), unsafe.Pointer(&value)) + } + + if err != nil { + // key not found in bpf maps + return nil + } + + proc = findConnProcess(&value, k) + + log.Debug("[ebpf conn] adding item to cache: %s", k) + ebpfCache.addNewItem(k, key, *proc) + if delItemIfFound { + deleteEbpfEntry(proto, unsafe.Pointer(&key[0])) + } + return +} + +// findConnProcess finds the process' details of a connection. +// By default we only receive the PID of the process, so we need to get +// the rest of the details. +// TODO: get the details from kernel, with mm_struct (exe_file, fd_path, etc). +func findConnProcess(value *networkEventT, connKey string) (proc *procmon.Process) { + comm := byteArrayToString(value.Comm[:]) + proc = procmon.NewProcess(int(value.Pid), comm) + // Use socket's UID. A process may have dropped privileges. + // This is the UID that we've always used. + proc.UID = int(value.UID) + + err := proc.ReadPath() + if ev, found := execEvents.isInStore(uint32(value.Pid)); found { + // use socket's UID. See above why ^ + ev.Proc.UID = proc.UID + ev.Proc.ReadCmdline() + // if proc's ReadPath() has been successfull, and the path received via the execve tracepoint differs, + // use proc's path. + // Sometimes we received from the tracepoint a wrong/non-existent path. + // Othertimes we receive a "helper" that executes the real binary which opens the connection. + // Downsides: for execveat() executions we won't display the original binary. + if err == nil && ev.Proc.Path != proc.Path { + proc.ReadCmdline() + ev.Proc.Path = proc.Path + ev.Proc.Args = proc.Args + } + proc = &ev.Proc + + log.Debug("[ebpf conn] not in cache, but in execEvents: %s, %d -> %s", connKey, proc.ID, proc.Path) + } else { + log.Debug("[ebpf conn] not in cache, NOR in execEvents: %s, %d -> %s", connKey, proc.ID, proc.Path) + // We'll end here if the events module has not been loaded, or if the process is not in cache. + proc.GetInfo() + execEvents.add(uint32(value.Pid), + *NewExecEvent(uint32(value.Pid), 0, uint32(value.UID), proc.Path, value.Comm), + *proc) + } + + return +} + +// FindInAlreadyEstablishedTCP searches those TCP connections which were already established at the time +// when opensnitch started +func findInAlreadyEstablishedTCP(proto string, srcPort uint, srcIP net.IP, dstIP net.IP, dstPort uint) (int, int, error) { + alreadyEstablished.RLock() + defer alreadyEstablished.RUnlock() + + var _alreadyEstablished map[*daemonNetlink.Socket]int + if proto == "tcp" { + _alreadyEstablished = alreadyEstablished.TCP + } else if proto == "tcp6" { + _alreadyEstablished = alreadyEstablished.TCPv6 + } + + for sock, v := range _alreadyEstablished { + if (*sock).ID.SourcePort == uint16(srcPort) && (*sock).ID.Source.Equal(srcIP) && + (*sock).ID.Destination.Equal(dstIP) && (*sock).ID.DestinationPort == uint16(dstPort) { + return v, int((*sock).UID), nil + } + } + return -1, -1, fmt.Errorf("eBPF inode not found") +} + +//returns true if addr is in the list of this machine's addresses +func findAddressInLocalAddresses(addr net.IP) bool { + lock.Lock() + defer lock.Unlock() + _, found := localAddresses[addr.String()] + return found +} diff --git a/daemon/procmon/ebpf/monitor.go b/daemon/procmon/ebpf/monitor.go new file mode 100644 index 0000000..2f15483 --- /dev/null +++ b/daemon/procmon/ebpf/monitor.go @@ -0,0 +1,163 @@ +package ebpf + +import ( + "syscall" + "time" + + "github.com/evilsocket/opensnitch/daemon/core" + "github.com/evilsocket/opensnitch/daemon/log" + daemonNetlink "github.com/evilsocket/opensnitch/daemon/netlink" + "github.com/vishvananda/netlink" +) + +// we need to manually remove old connections from a bpf map +// since when a bpf map is full it doesn't allow any more insertions +func monitorMaps() { + for { + select { + case <-ctxTasks.Done(): + goto Exit + default: + time.Sleep(time.Second * 5) + for name := range ebpfMaps { + // using a pointer to the map doesn't delete the items. + // bpftool still counts them. + if items := getItems(name, name == "tcp6" || name == "udp6"); items > 500 { + deleted := deleteOldItems(name, name == "tcp6" || name == "udp6", items/2) + log.Debug("[ebpf] old items deleted: %d", deleted) + } + } + } + } +Exit: +} + +func monitorCache() { + for { + select { + case <-ctxTasks.Done(): + goto Exit + case <-ebpfCacheTicker.C: + ebpfCache.DeleteOldItems() + execEvents.DeleteOldItems() + } + } +Exit: +} + +// maintain a list of this machine's local addresses +func monitorLocalAddresses() { + newAddrChan := make(chan netlink.AddrUpdate) + done := make(chan struct{}) + defer close(done) + + lock.Lock() + localAddresses = daemonNetlink.GetLocalAddrs() + lock.Unlock() + + netlink.AddrSubscribeWithOptions(newAddrChan, done, + netlink.AddrSubscribeOptions{ + ErrorCallback: func(err error) { + log.Error("AddrSubscribeWithOptions error: %s", err) + }, + ListExisting: true, + }) + + for { + select { + case <-ctxTasks.Done(): + done <- struct{}{} + goto Exit + case addr := <-newAddrChan: + if addr.NewAddr && !findAddressInLocalAddresses(addr.LinkAddress.IP) { + log.Debug("local addr added: %+v\n", addr) + lock.Lock() + + localAddresses[addr.LinkAddress.IP.String()] = daemonNetlink.AddrUpdateToAddr(&addr) + + lock.Unlock() + } else if !addr.NewAddr { + log.Debug("local addr removed: %+v\n", addr) + lock.Lock() + delete(localAddresses, addr.LinkAddress.IP.String()) + lock.Unlock() + } + } + } +Exit: + log.Debug("monitorLocalAddresses exited") +} + +// monitorAlreadyEstablished makes sure that when an already-established connection is closed +// it will be removed from alreadyEstablished. If we don't do this and keep the alreadyEstablished entry forever, +// then after the genuine process quits,a malicious process may reuse PID-srcPort-srcIP-dstPort-dstIP +func monitorAlreadyEstablished() { + tcperr := 0 + errLimitExceeded := func() bool { + if tcperr > 100 { + log.Debug("monitorAlreadyEstablished() generated too much errors") + return true + } + tcperr++ + + return false + } + + for { + select { + case <-ctxTasks.Done(): + goto Exit + default: + time.Sleep(time.Second * 2) + socketListTCP, err := daemonNetlink.SocketsDump(uint8(syscall.AF_INET), uint8(syscall.IPPROTO_TCP)) + if err != nil { + log.Debug("monitorAlreadyEstablished(), error dumping TCP sockets via netlink (%d): %s", tcperr, err) + if errLimitExceeded() { + goto Exit + } + continue + } + alreadyEstablished.Lock() + for aesock := range alreadyEstablished.TCP { + found := false + for _, sock := range socketListTCP { + if daemonNetlink.SocketsAreEqual(aesock, sock) { + found = true + break + } + } + if !found { + delete(alreadyEstablished.TCP, aesock) + } + } + alreadyEstablished.Unlock() + + if core.IPv6Enabled { + socketListTCPv6, err := daemonNetlink.SocketsDump(uint8(syscall.AF_INET6), uint8(syscall.IPPROTO_TCP)) + if err != nil { + if errLimitExceeded() { + goto Exit + } + log.Debug("monitorAlreadyEstablished(), error dumping TCPv6 sockets via netlink (%d): %s", tcperr, err) + continue + } + alreadyEstablished.Lock() + for aesock := range alreadyEstablished.TCPv6 { + found := false + for _, sock := range socketListTCPv6 { + if daemonNetlink.SocketsAreEqual(aesock, sock) { + found = true + break + } + } + if !found { + delete(alreadyEstablished.TCPv6, aesock) + } + } + alreadyEstablished.Unlock() + } + } + } +Exit: + log.Debug("monitorAlreadyEstablished exited") +} diff --git a/daemon/procmon/ebpf/utils.go b/daemon/procmon/ebpf/utils.go new file mode 100644 index 0000000..874e858 --- /dev/null +++ b/daemon/procmon/ebpf/utils.go @@ -0,0 +1,164 @@ +package ebpf + +import ( + "bytes" + "encoding/binary" + "fmt" + "unsafe" + + "github.com/evilsocket/opensnitch/daemon/core" + "github.com/evilsocket/opensnitch/daemon/log" +) + +func determineHostByteOrder() { + lock.Lock() + //determine host byte order + buf := [2]byte{} + *(*uint16)(unsafe.Pointer(&buf[0])) = uint16(0xABCD) + switch buf { + case [2]byte{0xCD, 0xAB}: + hostByteOrder = binary.LittleEndian + case [2]byte{0xAB, 0xCD}: + hostByteOrder = binary.BigEndian + default: + log.Error("Could not determine host byte order.") + } + lock.Unlock() +} + +func mountDebugFS() error { + debugfsPath := "/sys/kernel/debug/" + kprobesPath := fmt.Sprint(debugfsPath, "tracing/kprobe_events") + if core.Exists(kprobesPath) == false { + if _, err := core.Exec("mount", []string{"-t", "debugfs", "none", debugfsPath}); err != nil { + log.Warning("eBPF debugfs error: %s", err) + return fmt.Errorf(`%s +Unable to access debugfs filesystem, needed for eBPF to work, likely caused by a hardened or customized kernel. +Change process monitor method to 'proc' to stop receiving this alert + `, err) + } + } + + return nil +} + +// Trim null characters, and return the left part of the byte array. +// NOTE: using BPF_MAP_TYPE_PERCPU_ARRAY does not initialize strings to 0, +// so we end up receiving events as follow: +// event.filename -> /usr/bin/iptables +// event.filename -> /bin/lsn/iptables (should be /bin/ls) +// It turns out, that there's a 0x00 character between "/bin/ls" and "n/iptables": +// [47 115 98 105 110 47 100 117 109 112 101 50 102 115 0 0 101 115 +// ^^^ +// TODO: investigate if there's any way of initializing the struct to 0 +// like using __builtin_memset() (can't be used with PERCPU apparently) +func byteArrayToString(arr []byte) string { + temp := bytes.SplitAfter(arr, []byte("\x00"))[0] + return string(bytes.Trim(temp[:], "\x00")) +} + +func deleteEbpfEntry(proto string, key unsafe.Pointer) bool { + if err := m.DeleteElement(ebpfMaps[proto].bpfmap, key); err != nil { + log.Debug("error deleting ebpf entry: %s", err) + return false + } + return true +} + +func getItems(proto string, isIPv6 bool) (items uint) { + isDup := make(map[string]uint8) + var lookupKey []byte + var nextKey []byte + + if !isIPv6 { + lookupKey = make([]byte, 12) + nextKey = make([]byte, 12) + } else { + lookupKey = make([]byte, 36) + nextKey = make([]byte, 36) + } + var value networkEventT + firstrun := true + + for { + mp, ok := ebpfMaps[proto] + if !ok { + return + } + ok, err := m.LookupNextElement(mp.bpfmap, unsafe.Pointer(&lookupKey[0]), + unsafe.Pointer(&nextKey[0]), unsafe.Pointer(&value)) + if !ok || err != nil { //reached end of map + log.Debug("[ebpf] %s map: %d active items", proto, items) + return + } + if firstrun { + // on first run lookupKey is a dummy, nothing to delete + firstrun = false + copy(lookupKey, nextKey) + continue + } + if counter, duped := isDup[string(lookupKey)]; duped && counter > 1 { + deleteEbpfEntry(proto, unsafe.Pointer(&lookupKey[0])) + continue + } + isDup[string(lookupKey)]++ + copy(lookupKey, nextKey) + items++ + } + + return items +} + +// deleteOldItems deletes maps' elements in order to keep them below maximum capacity. +// If ebpf maps are full they don't allow any more insertions, ending up lossing events. +func deleteOldItems(proto string, isIPv6 bool, maxToDelete uint) (deleted uint) { + isDup := make(map[string]uint8) + var lookupKey []byte + var nextKey []byte + if !isIPv6 { + lookupKey = make([]byte, 12) + nextKey = make([]byte, 12) + } else { + lookupKey = make([]byte, 36) + nextKey = make([]byte, 36) + } + var value networkEventT + firstrun := true + i := uint(0) + + for { + i++ + if i > maxToDelete { + return + } + ok, err := m.LookupNextElement(ebpfMaps[proto].bpfmap, unsafe.Pointer(&lookupKey[0]), + unsafe.Pointer(&nextKey[0]), unsafe.Pointer(&value)) + if !ok || err != nil { //reached end of map + return + } + if _, duped := isDup[string(lookupKey)]; duped { + if deleteEbpfEntry(proto, unsafe.Pointer(&lookupKey[0])) { + deleted++ + copy(lookupKey, nextKey) + continue + } + return + } + + if firstrun { + // on first run lookupKey is a dummy, nothing to delete + firstrun = false + copy(lookupKey, nextKey) + continue + } + + if !deleteEbpfEntry(proto, unsafe.Pointer(&lookupKey[0])) { + return + } + deleted++ + isDup[string(lookupKey)]++ + copy(lookupKey, nextKey) + } + + return +} diff --git a/daemon/procmon/find.go b/daemon/procmon/find.go new file mode 100644 index 0000000..3bf4581 --- /dev/null +++ b/daemon/procmon/find.go @@ -0,0 +1,108 @@ +package procmon + +import ( + "os" + "sort" + "strconv" + + "github.com/evilsocket/opensnitch/daemon/core" +) + +func sortPidsByTime(fdList []os.FileInfo) []os.FileInfo { + sort.Slice(fdList, func(i, j int) bool { + t := fdList[i].ModTime().UnixNano() + u := fdList[j].ModTime().UnixNano() + return t > u + }) + return fdList +} + +// inodeFound searches for the given inode in /proc/<pid>/fd/ or +// /proc/<pid>/task/<tid>/fd/ and gets the symbolink link it points to, +// in order to compare it against the given inode. +// If the inode is found, the cache is updated ans sorted. +func inodeFound(pidsPath, expect, inodeKey string, inode, pid int) bool { + fdPath := core.ConcatStrings(pidsPath, strconv.Itoa(pid), "/fd/") + fdList := lookupPidDescriptors(fdPath, pid) + if fdList == nil { + return false + } + + for idx := 0; idx < len(fdList); idx++ { + descLink := core.ConcatStrings(fdPath, fdList[idx]) + if link, err := os.Readlink(descLink); err == nil && link == expect { + inodesCache.add(inodeKey, descLink, pid) + pidsCache.add(fdPath, fdList, pid) + return true + } + } + + return false +} + +// lookupPidInProc searches for an inode in /proc. +// First it gets the running PIDs and obtains the opened sockets. +// TODO: If the inode is not found, search again in the task/threads +// of every PID (costly). +func lookupPidInProc(pidsPath, expect, inodeKey string, inode int) int { + pidList := getProcPids(pidsPath) + for _, pid := range pidList { + if inodeFound(pidsPath, expect, inodeKey, inode, pid) { + return pid + } + } + return -1 +} + +// lookupPidDescriptors returns the list of descriptors inside +// /proc/<pid>/fd/ +// TODO: search in /proc/<pid>/task/<tid>/fd/ . +func lookupPidDescriptors(fdPath string, pid int) []string { + f, err := os.Open(fdPath) + if err != nil { + return nil + } + // This is where most of the time is wasted when looking for PIDs. + // long running processes like firefox/chrome tend to have a lot of descriptor + // references that points to non existent files on disk, but that remains in + // memory (those with " (deleted)"). + // This causes to have to iterate over 300 to 700 items, that are not sockets. + fdList, err := f.Readdir(-1) + f.Close() + if err != nil { + return nil + } + fdList = sortPidsByTime(fdList) + + s := make([]string, len(fdList)) + for n, f := range fdList { + s[n] = f.Name() + } + + return s +} + +// getProcPids returns the list of running PIDs, /proc or /proc/<pid>/task/ . +func getProcPids(pidsPath string) (pidList []int) { + f, err := os.Open(pidsPath) + if err != nil { + return pidList + } + ls, err := f.Readdir(-1) + f.Close() + if err != nil { + return pidList + } + ls = sortPidsByTime(ls) + + for _, f := range ls { + if f.IsDir() == false { + continue + } + if pid, err := strconv.Atoi(f.Name()); err == nil { + pidList = append(pidList, []int{pid}...) + } + } + + return pidList +} diff --git a/daemon/procmon/find_test.go b/daemon/procmon/find_test.go new file mode 100644 index 0000000..9588de8 --- /dev/null +++ b/daemon/procmon/find_test.go @@ -0,0 +1,42 @@ +package procmon + +import ( + "fmt" + "testing" +) + +func TestGetProcPids(t *testing.T) { + pids := getProcPids("/proc") + + if len(pids) == 0 { + t.Error("getProcPids() should not be 0", pids) + } +} + +func TestLookupPidDescriptors(t *testing.T) { + pidsFd := lookupPidDescriptors(fmt.Sprint("/proc/", myPid, "/fd/"), myPid) + if len(pidsFd) == 0 { + t.Error("getProcPids() should not be 0", pidsFd) + } +} + +func TestLookupPidInProc(t *testing.T) { + // we expect that the inode 1 points to /dev/null + expect := "/dev/null" + foundPid := lookupPidInProc("/proc/", expect, "", myPid) + if foundPid == -1 { + t.Error("lookupPidInProc() should not return -1") + } +} + +func BenchmarkGetProcs(b *testing.B) { + for i := 0; i < b.N; i++ { + getProcPids("/proc") + } +} + +func BenchmarkLookupPidDescriptors(b *testing.B) { + for i := 0; i < b.N; i++ { + lookupPidDescriptors(fmt.Sprint("/proc/", myPid, "/fd/"), myPid) + } +} diff --git a/daemon/procmon/monitor/init.go b/daemon/procmon/monitor/init.go new file mode 100644 index 0000000..85dfcbd --- /dev/null +++ b/daemon/procmon/monitor/init.go @@ -0,0 +1,112 @@ +package monitor + +import ( + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/evilsocket/opensnitch/daemon/procmon" + "github.com/evilsocket/opensnitch/daemon/procmon/audit" + "github.com/evilsocket/opensnitch/daemon/procmon/ebpf" +) + +var ( + cacheMonitorsRunning = false +) + +// List of errors that this package may return. +const ( + NoError = iota + ProcFsErr + AuditdErr + EbpfErr + EbpfEventsErr +) + +// Error wraps the type of error with its message +type Error struct { + What int + Msg error +} + +// ReconfigureMonitorMethod configures a new method for parsing connections. +func ReconfigureMonitorMethod(newMonitorMethod, ebpfModulesPath string) *Error { + if procmon.GetMonitorMethod() == newMonitorMethod { + return nil + } + + oldMethod := procmon.GetMonitorMethod() + if oldMethod == "" { + oldMethod = procmon.MethodProc + } + End() + procmon.SetMonitorMethod(newMonitorMethod) + // if the new monitor method fails to start, rollback the change and exit + // without saving the configuration. Otherwise we can end up with the wrong + // monitor method configured and saved to file. + err := Init(ebpfModulesPath) + if err.What > NoError { + log.Error("Reconf() -> Init() error: %v", err) + procmon.SetMonitorMethod(oldMethod) + return err + } + + return nil +} + +// End stops the way of parsing new connections. +func End() { + if procmon.MethodIsAudit() { + audit.Stop() + } else if procmon.MethodIsEbpf() { + ebpf.Stop() + } +} + +// Init starts parsing connections using the method specified. +func Init(ebpfModulesPath string) (errm *Error) { + errm = &Error{} + + if cacheMonitorsRunning == false { + go procmon.MonitorActivePids() + go procmon.CacheCleanerTask() + cacheMonitorsRunning = true + } + + if procmon.MethodIsEbpf() { + err := ebpf.Start(ebpfModulesPath) + if err == nil { + log.Info("Process monitor method ebpf") + return errm + } + // ebpf main module loaded, we can use ebpf + + // XXX: this will have to be rewritten when we'll have more events (bind, listen, etc) + if err.What == ebpf.EventsNotAvailable { + log.Info("Process monitor method ebpf") + log.Warning("opensnitch-procs.o not available: %s", err.Msg) + + return errm + } + + // we need to stop this method even if it has failed to start, in order to clean up the kprobes + // It helps with the error "cannot write...kprobe_events: file exists". + ebpf.Stop() + errm.What = err.What + errm.Msg = err.Msg + log.Warning("error starting ebpf monitor method: %v", err) + + } else if procmon.MethodIsAudit() { + auditConn, err := audit.Start() + if err == nil { + log.Info("Process monitor method audit") + go audit.Reader(auditConn, (chan<- audit.Event)(audit.EventChan)) + return &Error{AuditdErr, err} + } + errm.What = AuditdErr + errm.Msg = err + log.Warning("error starting audit monitor method: %v", err) + } + + // if any of the above methods have failed, fallback to proc + log.Info("Process monitor method /proc") + procmon.SetMonitorMethod(procmon.MethodProc) + return errm +} diff --git a/daemon/procmon/parse.go b/daemon/procmon/parse.go new file mode 100644 index 0000000..224ff16 --- /dev/null +++ b/daemon/procmon/parse.go @@ -0,0 +1,112 @@ +package procmon + +import ( + "fmt" + "net" + "time" + + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/evilsocket/opensnitch/daemon/netstat" + "github.com/evilsocket/opensnitch/daemon/procmon/audit" +) + +func getPIDFromAuditEvents(inode int, inodeKey string, expect string) (int, int) { + audit.Lock.RLock() + defer audit.Lock.RUnlock() + + auditEvents := audit.GetEvents() + for n := 0; n < len(auditEvents); n++ { + pid := auditEvents[n].Pid + if inodeFound("/proc/", expect, inodeKey, inode, pid) { + return pid, n + } + } + for n := 0; n < len(auditEvents); n++ { + ppid := auditEvents[n].PPid + if inodeFound("/proc/", expect, inodeKey, inode, ppid) { + return ppid, n + } + } + return -1, -1 +} + +// GetInodeFromNetstat tries to obtain the inode of a connection from /proc/net/* +func GetInodeFromNetstat(netEntry *netstat.Entry, inodeList *[]int, protocol string, srcIP net.IP, srcPort uint, dstIP net.IP, dstPort uint) bool { + if netEntry = netstat.FindEntry(protocol, srcIP, srcPort, dstIP, dstPort); netEntry == nil { + log.Debug("Could not find netstat entry for: (%s) %d:%s -> %s:%d", protocol, srcPort, srcIP, dstIP, dstPort) + return false + } + if netEntry.INode > 0 { + log.Debug("connection found in netstat: %#v", netEntry) + *inodeList = append([]int{netEntry.INode}, *inodeList...) + return true + } + log.Debug("<== no inodes found for this connection: %#v", netEntry) + + return false +} + +// GetPIDFromINode tries to get the PID from a socket inode following these steps: +// 1. Get the PID from the cache of Inodes. +// 2. Get the PID from the cache of PIDs. +// 3. Look for the PID using one of these methods: +// - audit: listening for socket creation from auditd. +// - proc: search /proc +// +// If the PID is not found by one of the 2 first methods, it'll try it using /proc. +func GetPIDFromINode(inode int, inodeKey string) int { + found := -1 + if inode <= 0 { + return found + } + start := time.Now() + + expect := fmt.Sprintf("socket:[%d]", inode) + if cachedPidInode := inodesCache.getPid(inodeKey); cachedPidInode != -1 { + log.Debug("Inode found in cache: %v %v %v %v", time.Since(start), inodesCache.getPid(inodeKey), inode, inodeKey) + return cachedPidInode + } + + cachedPid, pos := pidsCache.getPid(inode, inodeKey, expect) + if cachedPid != -1 { + log.Debug("Socket found in known pids %v, pid: %d, inode: %d, pos: %d, pids in cache: %d", time.Since(start), cachedPid, inode, pos, pidsCache.countItems()) + pidsCache.sort(cachedPid) + inodesCache.add(inodeKey, "", cachedPid) + return cachedPid + } + + if MethodIsAudit() { + if aPid, pos := getPIDFromAuditEvents(inode, inodeKey, expect); aPid != -1 { + log.Debug("PID found via audit events: %v, position: %d", time.Since(start), pos) + return aPid + } + } + if found == -1 || methodIsProc() { + found = lookupPidInProc("/proc/", expect, inodeKey, inode) + } + log.Debug("new pid lookup took (%d): %v", found, time.Since(start)) + + return found +} + +// FindProcess checks if a process exists given a PID. +// If it exists in /proc, a new Process{} object is returned with the details +// to identify a process (cmdline, name, environment variables, etc). +func FindProcess(pid int, interceptUnknown bool) *Process { + if interceptUnknown && pid < 0 { + return NewProcess(0, "") + } + + if proc := findProcessInActivePidsCache(uint64(pid)); proc != nil { + return proc + } + + proc := NewProcess(pid, "") + if err := proc.GetInfo(); err != nil { + log.Debug("[%d] FindProcess() error: %s", pid, err) + return nil + } + + AddToActivePidsCache(uint64(pid), proc) + return proc +} diff --git a/daemon/procmon/process.go b/daemon/procmon/process.go new file mode 100644 index 0000000..f6a2a65 --- /dev/null +++ b/daemon/procmon/process.go @@ -0,0 +1,164 @@ +package procmon + +import ( + "sync" + "time" + + "github.com/evilsocket/opensnitch/daemon/ui/protocol" +) + +var ( + cacheMonitorsRunning = false + lock = sync.RWMutex{} + monitorMethod = MethodProc +) + +// monitor method supported types +const ( + MethodProc = "proc" + MethodAudit = "audit" + MethodEbpf = "ebpf" + + KernelConnection = "Kernel connection" + ProcSelf = "/proc/self/" +) + +// man 5 proc; man procfs +type procIOstats struct { + RChar int64 + WChar int64 + SyscallRead int64 + SyscallWrite int64 + ReadBytes int64 + WriteBytes int64 +} + +type procNetStats struct { + ReadBytes uint64 + WriteBytes uint64 +} + +type procDescriptors struct { + ModTime time.Time + Name string + SymLink string + Size int64 +} + +type procStatm struct { + Size int64 + Resident int64 + Shared int64 + Text int64 + Lib int64 + Data int64 // data + stack + Dt int +} + +// Process holds the details of a process. +type Process struct { + Env map[string]string + IOStats *procIOstats + NetStats *procNetStats + Statm *procStatm + Maps string + // Path is the absolute path to the binary + Path string + Comm string + CWD string + Status string + Stat string + Stack string + Descriptors []*procDescriptors + // Args is the command that the user typed. It MAY contain the absolute path + // of the binary: + // $ curl https://... + // -> Path: /usr/bin/curl + // -> Args: curl https://.... + // $ /usr/bin/curl https://... + // -> Path: /usr/bin/curl + // -> Args: /usr/bin/curl https://.... + + Args []string + ID int + PPID int + UID int +} + +// NewProcess returns a new Process structure. +func NewProcess(pid int, comm string) *Process { + return &Process{ + ID: pid, + Comm: comm, + Args: make([]string, 0), + Env: make(map[string]string), + IOStats: &procIOstats{}, + NetStats: &procNetStats{}, + Statm: &procStatm{}, + } +} + +// Serialize transforms a Process object to gRPC protocol object +func (p *Process) Serialize() *protocol.Process { + ioStats := p.IOStats + netStats := p.NetStats + if ioStats == nil { + ioStats = &procIOstats{} + } + if netStats == nil { + netStats = &procNetStats{} + } + return &protocol.Process{ + Pid: uint64(p.ID), + Ppid: uint64(p.PPID), + Uid: uint64(p.UID), + Comm: p.Comm, + Path: p.Path, + Args: p.Args, + Env: p.Env, + Cwd: p.CWD, + IoReads: uint64(ioStats.RChar), + IoWrites: uint64(ioStats.WChar), + NetReads: netStats.ReadBytes, + NetWrites: netStats.WriteBytes, + } +} + +// SetMonitorMethod configures a new method for parsing connections. +func SetMonitorMethod(newMonitorMethod string) { + lock.Lock() + defer lock.Unlock() + + monitorMethod = newMonitorMethod +} + +// GetMonitorMethod configures a new method for parsing connections. +func GetMonitorMethod() string { + lock.Lock() + defer lock.Unlock() + + return monitorMethod +} + +// MethodIsEbpf returns if the process monitor method is eBPF. +func MethodIsEbpf() bool { + lock.RLock() + defer lock.RUnlock() + + return monitorMethod == MethodEbpf +} + +// MethodIsAudit returns if the process monitor method is eBPF. +func MethodIsAudit() bool { + lock.RLock() + defer lock.RUnlock() + + return monitorMethod == MethodAudit +} + +func methodIsProc() bool { + lock.RLock() + defer lock.RUnlock() + + return monitorMethod == MethodProc +} diff --git a/daemon/procmon/process_test.go b/daemon/procmon/process_test.go new file mode 100644 index 0000000..dd9b2a8 --- /dev/null +++ b/daemon/procmon/process_test.go @@ -0,0 +1,130 @@ +package procmon + +import ( + "os" + "testing" +) + +var ( + myPid = os.Getpid() + proc = NewProcess(myPid, "fakeComm") +) + +func TestNewProcess(t *testing.T) { + if proc.ID != myPid { + t.Error("NewProcess PID not equal to ", myPid) + } + if proc.Comm != "fakeComm" { + t.Error("NewProcess Comm not equal to fakeComm") + } +} + +func TestProcPath(t *testing.T) { + if err := proc.ReadPath(); err != nil { + t.Error("Proc path error:", err) + } + if proc.Path == "/fake/path" { + t.Error("Proc path equal to /fake/path, should be different:", proc.Path) + } +} + +func TestProcCwd(t *testing.T) { + err := proc.ReadCwd() + + if proc.CWD == "" { + t.Error("Proc readCwd() not read:", err) + } +} + +func TestProcCmdline(t *testing.T) { + proc.ReadCmdline() + + if len(proc.Args) == 0 { + t.Error("Proc Args should not be empty:", proc.Args) + } +} + +func TestProcDescriptors(t *testing.T) { + proc.readDescriptors() + + if len(proc.Descriptors) == 0 { + t.Error("Proc Descriptors should not be empty:", proc.Descriptors) + } +} + +func TestProcEnv(t *testing.T) { + proc.ReadEnv() + + if len(proc.Env) == 0 { + t.Error("Proc Env should not be empty:", proc.Env) + } +} + +func TestProcIOStats(t *testing.T) { + proc.readIOStats() + + if proc.IOStats.RChar == 0 { + t.Error("Proc.IOStats.RChar should not be 0:", proc.IOStats) + } + if proc.IOStats.WChar == 0 { + t.Error("Proc.IOStats.WChar should not be 0:", proc.IOStats) + } + if proc.IOStats.SyscallRead == 0 { + t.Error("Proc.IOStats.SyscallRead should not be 0:", proc.IOStats) + } + if proc.IOStats.SyscallWrite == 0 { + t.Error("Proc.IOStats.SyscallWrite should not be 0:", proc.IOStats) + } + /*if proc.IOStats.ReadBytes == 0 { + t.Error("Proc.IOStats.ReadBytes should not be 0:", proc.IOStats) + } + if proc.IOStats.WriteBytes == 0 { + t.Error("Proc.IOStats.WriteBytes should not be 0:", proc.IOStats) + }*/ +} + +func TestProcStatus(t *testing.T) { + proc.readStatus() + + if proc.Status == "" { + t.Error("Proc Status should not be empty:", proc) + } + if proc.Stat == "" { + t.Error("Proc Stat should not be empty:", proc) + } + /*if proc.Stack == "" { + t.Error("Proc Stack should not be empty:", proc) + }*/ + if proc.Maps == "" { + t.Error("Proc Maps should not be empty:", proc) + } + if proc.Statm.Size == 0 { + t.Error("Proc Statm Size should not be 0:", proc.Statm) + } + if proc.Statm.Resident == 0 { + t.Error("Proc Statm Resident should not be 0:", proc.Statm) + } + if proc.Statm.Shared == 0 { + t.Error("Proc Statm Shared should not be 0:", proc.Statm) + } + if proc.Statm.Text == 0 { + t.Error("Proc Statm Text should not be 0:", proc.Statm) + } + if proc.Statm.Lib != 0 { + t.Error("Proc Statm Lib should not be 0:", proc.Statm) + } + if proc.Statm.Data == 0 { + t.Error("Proc Statm Data should not be 0:", proc.Statm) + } + if proc.Statm.Dt != 0 { + t.Error("Proc Statm Dt should not be 0:", proc.Statm) + } +} + +func TestProcCleanPath(t *testing.T) { + proc.Path = "/fake/path/binary (deleted)" + proc.CleanPath() + if proc.Path != "/fake/path/binary" { + t.Error("Proc cleanPath() not cleaned:", proc.Path) + } +} diff --git a/daemon/rule/loader.go b/daemon/rule/loader.go new file mode 100644 index 0000000..dfa5f65 --- /dev/null +++ b/daemon/rule/loader.go @@ -0,0 +1,439 @@ +package rule + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path" + "path/filepath" + "sort" + "strings" + "sync" + "time" + + "github.com/evilsocket/opensnitch/daemon/conman" + "github.com/evilsocket/opensnitch/daemon/core" + "github.com/evilsocket/opensnitch/daemon/log" + + "github.com/fsnotify/fsnotify" +) + +// Loader is the object that holds the rules loaded from disk, as well as the +// rules watcher. +type Loader struct { + rules map[string]*Rule + watcher *fsnotify.Watcher + path string + rulesKeys []string + sync.RWMutex + liveReload bool + liveReloadRunning bool +} + +// NewLoader loads rules from disk, and watches for changes made to the rules files +// on disk. +func NewLoader(liveReload bool) (*Loader, error) { + watcher, err := fsnotify.NewWatcher() + if err != nil { + return nil, err + } + return &Loader{ + path: "", + rules: make(map[string]*Rule), + liveReload: liveReload, + watcher: watcher, + liveReloadRunning: false, + }, nil +} + +// NumRules returns he number of loaded rules. +func (l *Loader) NumRules() int { + l.RLock() + defer l.RUnlock() + return len(l.rules) +} + +// GetAll returns the loaded rules. +func (l *Loader) GetAll() map[string]*Rule { + l.RLock() + defer l.RUnlock() + return l.rules +} + +// Load loads rules files from disk. +func (l *Loader) Load(path string) error { + if core.Exists(path) == false { + return fmt.Errorf("Path '%s' does not exist\nCreate it if you want to save rules to disk", path) + } + path, err := core.ExpandPath(path) + if err != nil { + return fmt.Errorf("Error accessing rules path: %s.\nCreate it if you want to save rules to disk", err) + } + + expr := filepath.Join(path, "*.json") + matches, err := filepath.Glob(expr) + if err != nil { + return fmt.Errorf("Error globbing '%s': %s", expr, err) + } + + l.path = path + if len(l.rules) == 0 { + l.rules = make(map[string]*Rule) + } + + for _, fileName := range matches { + log.Debug("Reading rule from %s", fileName) + + if err := l.loadRule(fileName); err != nil { + log.Warning("%s", err) + continue + } + } + + if l.liveReload && l.liveReloadRunning == false { + go l.liveReloadWorker() + } + + return nil +} + +// Add adds a rule to the list of rules, and optionally saves it to disk. +func (l *Loader) Add(rule *Rule, saveToDisk bool) error { + l.addUserRule(rule) + if saveToDisk { + fileName := filepath.Join(l.path, fmt.Sprintf("%s.json", rule.Name)) + return l.Save(rule, fileName) + } + return nil +} + +// Replace adds a rule to the list of rules, and optionally saves it to disk. +func (l *Loader) Replace(rule *Rule, saveToDisk bool) error { + if err := l.replaceUserRule(rule); err != nil { + return err + } + if saveToDisk { + l.Lock() + defer l.Unlock() + + fileName := filepath.Join(l.path, fmt.Sprintf("%s.json", rule.Name)) + return l.Save(rule, fileName) + } + return nil +} + +// Save a rule to disk. +func (l *Loader) Save(rule *Rule, path string) error { + rule.Updated = time.Now().Format(time.RFC3339) + raw, err := json.MarshalIndent(rule, "", " ") + if err != nil { + return fmt.Errorf("Error while saving rule %s to %s: %s", rule, path, err) + } + + if err = ioutil.WriteFile(path, raw, 0600); err != nil { + return fmt.Errorf("Error while saving rule %s to %s: %s", rule, path, err) + } + + return nil +} + +// Delete deletes a rule from the list by name. +// If the duration is Always (i.e: saved on disk), it'll attempt to delete +// it from disk. +func (l *Loader) Delete(ruleName string) error { + l.Lock() + defer l.Unlock() + + rule := l.rules[ruleName] + if rule == nil { + return nil + } + l.cleanListsRule(rule) + + delete(l.rules, ruleName) + l.sortRules() + + if rule.Duration != Always { + return nil + } + + log.Info("Delete() rule: %s", rule) + return l.deleteRuleFromDisk(ruleName) +} + +func (l *Loader) loadRule(fileName string) error { + raw, err := ioutil.ReadFile(fileName) + if err != nil { + return fmt.Errorf("Error while reading %s: %s", fileName, err) + } + l.Lock() + defer l.Unlock() + + var r Rule + err = json.Unmarshal(raw, &r) + if err != nil { + return fmt.Errorf("Error parsing rule from %s: %s", fileName, err) + } + raw = nil + + if oldRule, found := l.rules[r.Name]; found { + l.cleanListsRule(oldRule) + } + + if !r.Enabled { + // XXX: we only parse and load the Data field if the rule is disabled and the Data field is not empty + // the rule will remain disabled. + if err = l.unmarshalOperatorList(&r.Operator); err != nil { + return err + } + } else { + if err := r.Operator.Compile(); err != nil { + log.Warning("Operator.Compile() error: %s: %s (%s)", err, r.Operator.Data, r.Name) + return fmt.Errorf("(1) Error compiling rule: %s", err) + } + if r.Operator.Type == List { + for i := 0; i < len(r.Operator.List); i++ { + if err := r.Operator.List[i].Compile(); err != nil { + log.Warning("Operator.Compile() error: %s (%s)", err, r.Name) + return fmt.Errorf("(1) Error compiling list rule: %s", err) + } + } + } + } + if oldRule, found := l.rules[r.Name]; found { + l.deleteOldRuleFromDisk(oldRule, &r) + } + + log.Debug("Loaded rule from %s: %s", fileName, r.String()) + l.rules[r.Name] = &r + l.sortRules() + + if l.isTemporary(&r) { + err = l.scheduleTemporaryRule(r) + } + + return nil +} + +// deleteRule deletes a rule from memory if it has been deleted from disk. +// This is only called if fsnotify's Remove event is fired, thus it doesn't +// have to delete temporary rules (!Always). +func (l *Loader) deleteRule(filePath string) { + fileName := filepath.Base(filePath) + ruleName := fileName[:len(fileName)-5] + + l.RLock() + rule, found := l.rules[ruleName] + delRule := found && rule.Duration == Always + l.RUnlock() + if delRule { + l.Delete(ruleName) + } +} + +func (l *Loader) deleteRuleFromDisk(ruleName string) error { + path := fmt.Sprint(l.path, "/", ruleName, ".json") + return os.Remove(path) +} + +// deleteOldRuleFromDisk deletes a rule from disk if the Duration changes +// from Always (saved on disk), to !Always (temporary). +func (l *Loader) deleteOldRuleFromDisk(oldRule, newRule *Rule) { + if oldRule.Duration == Always && newRule.Duration != Always { + if err := l.deleteRuleFromDisk(oldRule.Name); err != nil { + log.Error("Error deleting old rule from disk: %s", oldRule.Name) + } + } +} + +// cleanListsRule erases the list of domains of an Operator of type Lists +func (l *Loader) cleanListsRule(oldRule *Rule) { + if oldRule.Operator.Type == Lists { + oldRule.Operator.StopMonitoringLists() + } else if oldRule.Operator.Type == List { + for i := 0; i < len(oldRule.Operator.List); i++ { + if oldRule.Operator.List[i].Type == Lists { + oldRule.Operator.List[i].StopMonitoringLists() + break + } + } + } +} + +func (l *Loader) isTemporary(r *Rule) bool { + return r.Duration != Restart && r.Duration != Always && r.Duration != Once +} + +func (l *Loader) isUniqueName(name string) bool { + _, found := l.rules[name] + return !found +} + +func (l *Loader) setUniqueName(rule *Rule) { + l.Lock() + defer l.Unlock() + + idx := 1 + base := rule.Name + for l.isUniqueName(rule.Name) == false { + idx++ + rule.Name = fmt.Sprintf("%s-%d", base, idx) + } +} + +// Deprecated: rule.Operator.Data no longer holds the operator list in json format as string. +func (l *Loader) unmarshalOperatorList(op *Operator) error { + if op.Type == List && len(op.List) == 0 && op.Data != "" { + if err := json.Unmarshal([]byte(op.Data), &op.List); err != nil { + return fmt.Errorf("error loading rule of type list: %s", err) + } + op.Data = "" + } + + return nil +} + +func (l *Loader) sortRules() { + l.rulesKeys = make([]string, 0, len(l.rules)) + for k := range l.rules { + l.rulesKeys = append(l.rulesKeys, k) + } + sort.Strings(l.rulesKeys) +} + +func (l *Loader) addUserRule(rule *Rule) { + if rule.Duration == Once { + return + } + + l.setUniqueName(rule) + l.replaceUserRule(rule) +} + +func (l *Loader) replaceUserRule(rule *Rule) (err error) { + l.Lock() + oldRule, found := l.rules[rule.Name] + l.Unlock() + + if found { + // If the rule has changed from Always (saved on disk) to !Always (temporary), + // we need to delete the rule from disk and keep it in memory. + l.deleteOldRuleFromDisk(oldRule, rule) + + // delete loaded lists, if this is a rule of type Lists + l.cleanListsRule(oldRule) + } + + if err := l.unmarshalOperatorList(&rule.Operator); err != nil { + log.Error(err.Error()) + } + + if rule.Enabled { + if err := rule.Operator.Compile(); err != nil { + log.Warning("Operator.Compile() error: %s: %s", err, rule.Operator.Data) + return fmt.Errorf("(2) error compiling rule: %s", err) + } + + if rule.Operator.Type == List { + for i := 0; i < len(rule.Operator.List); i++ { + if err := rule.Operator.List[i].Compile(); err != nil { + log.Warning("Operator.Compile() error: %s: ", err) + return fmt.Errorf("(2) error compiling list rule: %s", err) + } + } + } + } + l.Lock() + l.rules[rule.Name] = rule + l.sortRules() + l.Unlock() + + if l.isTemporary(rule) { + err = l.scheduleTemporaryRule(*rule) + } + + return err +} + +func (l *Loader) scheduleTemporaryRule(rule Rule) error { + tTime, err := time.ParseDuration(string(rule.Duration)) + if err != nil { + return err + } + + time.AfterFunc(tTime, func() { + l.Lock() + defer l.Unlock() + + log.Info("Temporary rule expired: %s - %s", rule.Name, rule.Duration) + if newRule, found := l.rules[rule.Name]; found { + if newRule.Duration != rule.Duration { + log.Debug("%s temporary rule expired, but has new Duration, old: %s, new: %s", rule.Name, rule.Duration, newRule.Duration) + return + } + delete(l.rules, rule.Name) + l.sortRules() + } + }) + return nil +} + +func (l *Loader) liveReloadWorker() { + l.liveReloadRunning = true + + log.Debug("Rules watcher started on path %s ...", l.path) + if err := l.watcher.Add(l.path); err != nil { + log.Error("Could not watch path: %s", err) + l.liveReloadRunning = false + return + } + + for { + select { + case event := <-l.watcher.Events: + // a new rule json file has been created or updated + if event.Op&fsnotify.Write == fsnotify.Write { + if strings.HasSuffix(event.Name, ".json") { + log.Important("Ruleset changed due to %s, reloading ...", path.Base(event.Name)) + if err := l.loadRule(event.Name); err != nil { + log.Warning("%s", err) + } + } + } else if event.Op&fsnotify.Remove == fsnotify.Remove { + if strings.HasSuffix(event.Name, ".json") { + log.Important("Rule deleted %s", path.Base(event.Name)) + // we only need to delete from memory rules of type Always, + // because the Remove event is of a file, i.e.: Duration == Always + l.deleteRule(event.Name) + } + } + case err := <-l.watcher.Errors: + log.Error("File system watcher error: %s", err) + } + } +} + +// FindFirstMatch will try match the connection against the existing rule set. +func (l *Loader) FindFirstMatch(con *conman.Connection) (match *Rule) { + l.RLock() + defer l.RUnlock() + + for _, idx := range l.rulesKeys { + rule, _ := l.rules[idx] + if rule.Enabled == false { + continue + } + if rule.Match(con) { + // We have a match. + // Save the rule in order to don't ask the user to take action, + // and keep iterating until a Deny or a Priority rule appears. + match = rule + if rule.Action == Reject || rule.Action == Deny || rule.Precedence == true { + return rule + } + } + } + + return match +} diff --git a/daemon/rule/loader_test.go b/daemon/rule/loader_test.go new file mode 100644 index 0000000..37d958d --- /dev/null +++ b/daemon/rule/loader_test.go @@ -0,0 +1,327 @@ +package rule + +import ( + "fmt" + "io" + "math/rand" + "os" + "testing" + "time" +) + +var tmpDir string + +func TestMain(m *testing.M) { + tmpDir = "/tmp/ostest_" + randString() + os.Mkdir(tmpDir, 0777) + defer os.RemoveAll(tmpDir) + os.Exit(m.Run()) +} + +func TestRuleLoader(t *testing.T) { + t.Parallel() + t.Log("Test rules loader") + + var list []Operator + dur1s := Duration("1s") + dummyOper, _ := NewOperator(Simple, false, OpTrue, "", list) + dummyOper.Compile() + inMem1sRule := Create("000-xxx-name", "rule description xxx", true, false, false, Allow, dur1s, dummyOper) + inMemUntilRestartRule := Create("000-aaa-name", "rule description aaa", true, false, false, Allow, Restart, dummyOper) + + l, err := NewLoader(false) + if err != nil { + t.Fail() + } + if err = l.Load("/non/existent/path/"); err == nil { + t.Error("non existent path test: err should not be nil") + } + + if err = l.Load("testdata/"); err != nil { + t.Error("Error loading test rules: ", err) + } + // we expect 6 valid rules (2 invalid), loaded from testdata/ + testNumRules(t, l, 6) + + if err = l.Add(inMem1sRule, false); err != nil { + t.Error("Error adding temporary rule") + } + testNumRules(t, l, 7) + + // test auto deletion of temporary rule + time.Sleep(time.Second * 2) + testNumRules(t, l, 6) + + if err = l.Add(inMemUntilRestartRule, false); err != nil { + t.Error("Error adding temporary rule (2)") + } + testNumRules(t, l, 7) + testRulesOrder(t, l) + testSortRules(t, l) + testFindMatch(t, l) + testFindEnabled(t, l) + testDurationChange(t, l) +} + +func TestRuleLoaderInvalidRegexp(t *testing.T) { + t.Parallel() + t.Log("Test rules loader: invalid regexp") + + l, err := NewLoader(true) + if err != nil { + t.Fail() + } + t.Run("loadRule() from disk test (simple)", func(t *testing.T) { + if err := l.loadRule("testdata/invalid-regexp.json"); err == nil { + t.Error("invalid regexp rule loaded: loadRule()") + } + }) + + t.Run("loadRule() from disk test (list)", func(t *testing.T) { + if err := l.loadRule("testdata/invalid-regexp-list.json"); err == nil { + t.Error("invalid regexp rule loaded: loadRule()") + } + }) + + var list []Operator + dur30m := Duration("30m") + opListData := `[{"type": "regexp", "operand": "process.path", "sensitive": false, "data": "^(/di(rmngr)$"}, {"type": "simple", "operand": "dest.port", "data": "53", "sensitive": false}]` + invalidRegexpOp, _ := NewOperator(List, false, OpList, opListData, list) + invalidRegexpRule := Create("invalid-regexp", "invalid rule description", true, false, false, Allow, dur30m, invalidRegexpOp) + + t.Run("replaceUserRule() test list", func(t *testing.T) { + if err := l.replaceUserRule(invalidRegexpRule); err == nil { + t.Error("invalid regexp rule loaded: replaceUserRule()") + } + }) +} + +// Test rules of type operator.list. There're these scenarios: +// - Enabled rules: +// * operator Data field is ignored if it contains the list of operators as json string. +// * the operarots list is expanded as json objecs under "list": [] +// For new rules (> v1.6.3), Data field will be empty. +// +// - Disabled rules +// * (old) the Data field contains the list of operators as json string, and the list of operarots is empty. +// * Data field empty, and the list of operators expanded. +// In all cases the list of operators must be loaded. +func TestRuleLoaderList(t *testing.T) { + l, err := NewLoader(true) + if err != nil { + t.Fail() + } + + testRules := map[string]string{ + "rule-with-operator-list": "testdata/rule-operator-list.json", + "rule-disabled-with-operators-list-as-json-string": "testdata/rule-disabled-operator-list.json", + "rule-disabled-with-operators-list-expanded": "testdata/rule-disabled-operator-list-expanded.json", + "rule-with-operator-list-data-empty": "testdata/rule-operator-list-data-empty.json", + } + + for name, path := range testRules { + t.Run(fmt.Sprint("loadRule() ", path), func(t *testing.T) { + if err := l.loadRule(path); err != nil { + t.Error(fmt.Sprint("loadRule() ", path, " error:"), err) + } + t.Log("Test: List rule:", name, path) + r, found := l.rules[name] + if !found { + t.Error(fmt.Sprint("loadRule() ", path, " not in the list:"), l.rules) + } + // Starting from > v1.6.3, after loading a rule of type List, the field Operator.Data is emptied, if the Data contained the list of operators as json. + if len(r.Operator.List) != 2 { + t.Error(fmt.Sprint("loadRule() ", path, " operator List not loaded:"), r) + } + if r.Operator.List[0].Type != Simple || + r.Operator.List[0].Operand != OpProcessPath || + r.Operator.List[0].Data != "/usr/bin/telnet" { + t.Error(fmt.Sprint("loadRule() ", path, " operator List 0 not loaded:"), r) + } + if r.Operator.List[1].Type != Simple || + r.Operator.List[1].Operand != OpDstPort || + r.Operator.List[1].Data != "53" { + t.Error(fmt.Sprint("loadRule() ", path, " operator List 1 not loaded:"), r) + } + }) + } +} + +func TestLiveReload(t *testing.T) { + t.Parallel() + t.Log("Test rules loader with live reload") + l, err := NewLoader(true) + if err != nil { + t.Fail() + } + if err = Copy("testdata/000-allow-chrome.json", tmpDir+"/000-allow-chrome.json"); err != nil { + t.Error("Error copying rule into a temp dir") + } + if err = Copy("testdata/001-deny-chrome.json", tmpDir+"/001-deny-chrome.json"); err != nil { + t.Error("Error copying rule into a temp dir") + } + if err = l.Load(tmpDir); err != nil { + t.Error("Error loading test rules: ", err) + } + //wait for watcher to activate + time.Sleep(time.Second) + if err = Copy("testdata/live_reload/test-live-reload-remove.json", tmpDir+"/test-live-reload-remove.json"); err != nil { + t.Error("Error copying rules into temp dir") + } + if err = Copy("testdata/live_reload/test-live-reload-delete.json", tmpDir+"/test-live-reload-delete.json"); err != nil { + t.Error("Error copying rules into temp dir") + } + //wait for watcher to pick up the changes + time.Sleep(time.Second) + testNumRules(t, l, 4) + if err = os.Remove(tmpDir + "/test-live-reload-remove.json"); err != nil { + t.Error("Error Remove()ing file from temp dir") + } + if err = l.Delete("test-live-reload-delete"); err != nil { + t.Error("Error Delete()ing file from temp dir") + } + //wait for watcher to pick up the changes + time.Sleep(time.Second) + testNumRules(t, l, 2) +} + +func randString() string { + rand.Seed(time.Now().UnixNano()) + var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + b := make([]rune, 10) + for i := range b { + b[i] = letterRunes[rand.Intn(len(letterRunes))] + } + return string(b) +} + +func Copy(src, dst string) error { + in, err := os.Open(src) + if err != nil { + return err + } + defer in.Close() + + out, err := os.Create(dst) + if err != nil { + return err + } + defer out.Close() + + _, err = io.Copy(out, in) + if err != nil { + return err + } + return out.Close() +} + +func testNumRules(t *testing.T, l *Loader, num int) { + if l.NumRules() != num { + t.Error("rules number should be (2): ", num) + } +} + +func testRulesOrder(t *testing.T, l *Loader) { + if l.rulesKeys[0] != "000-aaa-name" { + t.Error("Rules not in order (0): ", l.rulesKeys) + } + if l.rulesKeys[1] != "000-allow-chrome" { + t.Error("Rules not in order (1): ", l.rulesKeys) + } + if l.rulesKeys[2] != "001-deny-chrome" { + t.Error("Rules not in order (2): ", l.rulesKeys) + } +} + +func testSortRules(t *testing.T, l *Loader) { + l.rulesKeys[1] = "001-deny-chrome" + l.rulesKeys[2] = "000-allow-chrome" + l.sortRules() + if l.rulesKeys[1] != "000-allow-chrome" { + t.Error("Rules not in order (1): ", l.rulesKeys) + } + if l.rulesKeys[2] != "001-deny-chrome" { + t.Error("Rules not in order (2): ", l.rulesKeys) + } +} + +func testFindMatch(t *testing.T, l *Loader) { + conn.Process.Path = "/opt/google/chrome/chrome" + + testFindPriorityMatch(t, l) + testFindDenyMatch(t, l) + testFindAllowMatch(t, l) + + restoreConnection() +} + +func testFindPriorityMatch(t *testing.T, l *Loader) { + match := l.FindFirstMatch(conn) + if match == nil { + t.Error("FindPriorityMatch didn't match") + } + // test 000-allow-chrome, priority == true + if match.Name != "000-allow-chrome" { + t.Error("findPriorityMatch: priority rule failed: ", match) + } + +} + +func testFindDenyMatch(t *testing.T, l *Loader) { + l.rules["000-allow-chrome"].Precedence = false + // test 000-allow-chrome, priority == false + // 001-deny-chrome must match + match := l.FindFirstMatch(conn) + if match == nil { + t.Error("FindDenyMatch deny didn't match") + } + if match.Name != "001-deny-chrome" { + t.Error("findDenyMatch: deny rule failed: ", match) + } +} + +func testFindAllowMatch(t *testing.T, l *Loader) { + l.rules["000-allow-chrome"].Precedence = false + l.rules["001-deny-chrome"].Action = Allow + // test 000-allow-chrome, priority == false + // 001-deny-chrome must match + match := l.FindFirstMatch(conn) + if match == nil { + t.Error("FindAllowMatch allow didn't match") + } + if match.Name != "001-deny-chrome" { + t.Error("findAllowMatch: allow rule failed: ", match) + } +} + +func testFindEnabled(t *testing.T, l *Loader) { + l.rules["000-allow-chrome"].Precedence = false + l.rules["001-deny-chrome"].Action = Allow + l.rules["001-deny-chrome"].Enabled = false + // test 000-allow-chrome, priority == false + // 001-deny-chrome must match + match := l.FindFirstMatch(conn) + if match == nil { + t.Error("FindEnabledMatch, match nil") + } + if match.Name == "001-deny-chrome" { + t.Error("findEnabledMatch: deny rule shouldn't have matched: ", match) + } +} + +// test that changing the Duration of a temporary rule doesn't delete +// the new one, ignoring the old timer. +func testDurationChange(t *testing.T, l *Loader) { + l.rules["000-aaa-name"].Duration = "2s" + if err := l.replaceUserRule(l.rules["000-aaa-name"]); err != nil { + t.Error("testDurationChange, error replacing rule: ", err) + } + l.rules["000-aaa-name"].Duration = "1h" + if err := l.replaceUserRule(l.rules["000-aaa-name"]); err != nil { + t.Error("testDurationChange, error replacing rule: ", err) + } + time.Sleep(time.Second * 4) + if _, found := l.rules["000-aaa-name"]; !found { + t.Error("testDurationChange, error: rule has been deleted") + } +} diff --git a/daemon/rule/operator.go b/daemon/rule/operator.go new file mode 100644 index 0000000..021afc0 --- /dev/null +++ b/daemon/rule/operator.go @@ -0,0 +1,319 @@ +package rule + +import ( + "fmt" + "net" + "reflect" + "regexp" + "strconv" + "strings" + "sync" + + "github.com/evilsocket/opensnitch/daemon/conman" + "github.com/evilsocket/opensnitch/daemon/core" + "github.com/evilsocket/opensnitch/daemon/log" +) + +// Type is the type of rule. +// Every type has its own way of checking the user data against connections. +type Type string + +// Sensitive defines if a rule is case-sensitive or not. By default no. +type Sensitive bool + +// Operand is what we check on a connection. +type Operand string + +// Available types +const ( + Simple = Type("simple") + Regexp = Type("regexp") + Complex = Type("complex") // for future use + List = Type("list") + Network = Type("network") + Lists = Type("lists") +) + +// Available operands +const ( + OpTrue = Operand("true") + OpProcessID = Operand("process.id") + OpProcessPath = Operand("process.path") + OpProcessCmd = Operand("process.command") + OpProcessEnvPrefix = Operand("process.env.") + OpProcessEnvPrefixLen = 12 + OpUserID = Operand("user.id") + OpSrcIP = Operand("source.ip") + OpSrcPort = Operand("source.port") + OpDstIP = Operand("dest.ip") + OpDstHost = Operand("dest.host") + OpDstPort = Operand("dest.port") + OpDstNetwork = Operand("dest.network") + OpSrcNetwork = Operand("source.network") + OpProto = Operand("protocol") + OpIfaceIn = Operand("iface.in") + OpIfaceOut = Operand("iface.out") + OpList = Operand("list") + OpDomainsLists = Operand("lists.domains") + OpDomainsRegexpLists = Operand("lists.domains_regexp") + OpIPLists = Operand("lists.ips") + OpNetLists = Operand("lists.nets") +) + +type opCallback func(value interface{}) bool + +// Operator represents what we want to filter of a connection, and how. +type Operator struct { + cb opCallback + re *regexp.Regexp + netMask *net.IPNet + lists map[string]interface{} + exitMonitorChan chan (bool) + + Operand Operand `json:"operand"` + Data string `json:"data"` + Type Type `json:"type"` + List []Operator `json:"list"` + Sensitive Sensitive `json:"sensitive"` + + listsMonitorRunning bool + isCompiled bool + + sync.RWMutex +} + +// NewOperator returns a new operator object +func NewOperator(t Type, s Sensitive, o Operand, data string, list []Operator) (*Operator, error) { + op := Operator{ + Type: t, + Sensitive: s, + Operand: o, + Data: data, + List: list, + } + return &op, nil +} + +// Compile translates the operator type field to its callback counterpart +func (o *Operator) Compile() error { + if o.isCompiled { + return nil + } + if o.Type == Simple { + o.cb = o.simpleCmp + } else if o.Type == Regexp { + o.cb = o.reCmp + if o.Sensitive == false { + o.Data = strings.ToLower(o.Data) + } + re, err := regexp.Compile(o.Data) + if err != nil { + return err + } + o.re = re + } else if o.Type == List { + o.Operand = OpList + } else if o.Type == Network { + var err error + _, o.netMask, err = net.ParseCIDR(o.Data) + if err != nil { + return err + } + o.cb = o.cmpNetwork + } else if o.Type == Lists { + if o.Data == "" { + return fmt.Errorf("Operand lists is empty, nothing to load: %s", o) + } + + if o.Operand == OpDomainsLists { + o.loadLists() + o.cb = o.domainsListCmp + } else if o.Operand == OpDomainsRegexpLists { + o.loadLists() + o.cb = o.reListCmp + } else if o.Operand == OpIPLists { + o.loadLists() + o.cb = o.ipListCmp + } else if o.Operand == OpNetLists { + o.loadLists() + o.cb = o.ipNetCmp + } else { + return fmt.Errorf("Unknown Lists operand: %s", o.Operand) + } + + } else { + return fmt.Errorf("Unknown type: %s", o.Type) + } + + log.Debug("Operator compiled: %s", o) + o.isCompiled = true + + return nil +} + +func (o *Operator) String() string { + how := "is" + if o.Type == Regexp { + how = "matches" + } + return fmt.Sprintf("%s %s '%s'", log.Bold(string(o.Operand)), how, log.Yellow(string(o.Data))) +} + +func (o *Operator) simpleCmp(v interface{}) bool { + if o.Sensitive == false { + return strings.EqualFold(v.(string), o.Data) + } + return v == o.Data +} + +func (o *Operator) reCmp(v interface{}) bool { + if vt := reflect.ValueOf(v).Kind(); vt != reflect.String { + log.Warning("Operator.reCmp() bad interface type: %T", v) + return false + } + if o.Sensitive == false { + v = strings.ToLower(v.(string)) + } + return o.re.MatchString(v.(string)) +} + +func (o *Operator) cmpNetwork(destIP interface{}) bool { + // 192.0.2.1/24, 2001:db8:a0b:12f0::1/32 + if o.netMask == nil { + log.Warning("cmpNetwork() NULL: %s", destIP) + return false + } + return o.netMask.Contains(destIP.(net.IP)) +} + +func (o *Operator) domainsListCmp(v interface{}) bool { + dstHost := v.(string) + if dstHost == "" { + return false + } + if o.Sensitive == false { + dstHost = strings.ToLower(dstHost) + } + o.RLock() + defer o.RUnlock() + + if _, found := o.lists[dstHost]; found { + log.Debug("%s: %s, %s", log.Red("domain list match"), dstHost, o.lists[dstHost]) + return true + } + return false +} + +func (o *Operator) ipListCmp(v interface{}) bool { + dstIP := v.(string) + if dstIP == "" { + return false + } + o.RLock() + defer o.RUnlock() + + if _, found := o.lists[dstIP]; found { + log.Debug("%s: %s, %s", log.Red("IP list match"), dstIP, o.lists[dstIP].(string)) + return true + } + return false +} + +func (o *Operator) ipNetCmp(dstIP interface{}) bool { + o.RLock() + defer o.RUnlock() + + for host, netMask := range o.lists { + n := netMask.(*net.IPNet) + if n.Contains(dstIP.(net.IP)) { + log.Debug("%s: %s, %s", log.Red("Net list match"), dstIP, host) + return true + } + } + return false +} + +func (o *Operator) reListCmp(v interface{}) bool { + dstHost := v.(string) + if dstHost == "" { + return false + } + if o.Sensitive == false { + dstHost = strings.ToLower(dstHost) + } + o.RLock() + defer o.RUnlock() + + for file, re := range o.lists { + r := re.(*regexp.Regexp) + if r.MatchString(dstHost) { + log.Debug("%s: %s, %s", log.Red("Regexp list match"), dstHost, file) + return true + } + } + return false +} + +func (o *Operator) listMatch(con interface{}) bool { + res := true + for i := 0; i < len(o.List); i++ { + res = res && o.List[i].Match(con.(*conman.Connection)) + } + return res +} + +// Match tries to match parts of a connection with the given operator. +func (o *Operator) Match(con *conman.Connection) bool { + + if o.Operand == OpTrue { + return true + } else if o.Operand == OpList { + return o.listMatch(con) + } else if o.Operand == OpProcessPath { + return o.cb(con.Process.Path) + } else if o.Operand == OpProcessCmd { + return o.cb(strings.Join(con.Process.Args, " ")) + } else if o.Operand == OpDstHost && con.DstHost != "" { + return o.cb(con.DstHost) + } else if o.Operand == OpDstIP { + return o.cb(con.DstIP.String()) + } else if o.Operand == OpDstPort { + return o.cb(strconv.FormatUint(uint64(con.DstPort), 10)) + } else if o.Operand == OpDomainsLists { + return o.cb(con.DstHost) + } else if o.Operand == OpIPLists { + return o.cb(con.DstIP.String()) + } else if o.Operand == OpUserID { + return o.cb(strconv.Itoa(con.Entry.UserId)) + } else if o.Operand == OpDstNetwork { + return o.cb(con.DstIP) + } else if o.Operand == OpSrcNetwork { + return o.cb(con.SrcIP) + } else if o.Operand == OpNetLists { + return o.cb(con.DstIP) + } else if o.Operand == OpDomainsRegexpLists { + return o.cb(con.DstHost) + } else if o.Operand == OpIfaceIn { + if ifname, err := net.InterfaceByIndex(con.Pkt.IfaceInIdx); err == nil { + return o.cb(ifname.Name) + } + } else if o.Operand == OpIfaceOut { + if ifname, err := net.InterfaceByIndex(con.Pkt.IfaceOutIdx); err == nil { + return o.cb(ifname.Name) + } + } else if o.Operand == OpProto { + return o.cb(con.Protocol) + } else if o.Operand == OpSrcIP { + return o.cb(con.SrcIP.String()) + } else if o.Operand == OpSrcPort { + return o.cb(strconv.FormatUint(uint64(con.SrcPort), 10)) + } else if o.Operand == OpProcessID { + return o.cb(strconv.Itoa(con.Process.ID)) + } else if strings.HasPrefix(string(o.Operand), string(OpProcessEnvPrefix)) { + envVarName := core.Trim(string(o.Operand[OpProcessEnvPrefixLen:])) + envVarValue, _ := con.Process.Env[envVarName] + return o.cb(envVarValue) + } + + return false +} diff --git a/daemon/rule/operator_lists.go b/daemon/rule/operator_lists.go new file mode 100644 index 0000000..dd05635 --- /dev/null +++ b/daemon/rule/operator_lists.go @@ -0,0 +1,263 @@ +package rule + +import ( + "fmt" + "io/ioutil" + "net" + "path/filepath" + "regexp" + "runtime/debug" + "strings" + "time" + + "github.com/evilsocket/opensnitch/daemon/core" + "github.com/evilsocket/opensnitch/daemon/log" +) + +func (o *Operator) monitorLists() { + log.Info("monitor lists started: %s", o.Data) + + modTimes := make(map[string]time.Time) + totalFiles := 0 + needReload := false + numFiles := 0 + + expr := filepath.Join(o.Data, "/*.*") + for { + select { + case <-o.exitMonitorChan: + goto Exit + default: + fileList, err := filepath.Glob(expr) + if err != nil { + log.Warning("Error reading directory of domains list: %s, %s", o.Data, err) + goto Exit + } + numFiles = 0 + + for _, filename := range fileList { + // ignore hidden files + name := filepath.Base(filename) + if name[:1] == "." { + delete(modTimes, filename) + continue + } + // an overwrite operation performs two tasks: truncate the file and save the new content, + // causing the file time to be modified twice. + modTime, err := core.GetFileModTime(filename) + if err != nil { + log.Debug("deleting saved mod time due to error reading the list, %s", filename) + delete(modTimes, filename) + } else if lastModTime, found := modTimes[filename]; found { + if lastModTime.Equal(modTime) == false { + log.Debug("list changed: %s, %s, %s", lastModTime, modTime, filename) + needReload = true + } + } + modTimes[filename] = modTime + numFiles++ + } + fileList = nil + + if numFiles != totalFiles { + needReload = true + } + totalFiles = numFiles + + if needReload { + // we can't reload a single list, because the domains of all lists are added to the same map. + // we could have the domains separated by lists/files, but then we'd need to iterate the map in order + // to match a domain. Reloading the lists shoud only occur once a day. + if err := o.readLists(); err != nil { + log.Warning("%s", err) + } + needReload = false + } + time.Sleep(4 * time.Second) + } + } + +Exit: + modTimes = nil + o.ClearLists() + log.Info("lists monitor stopped") +} + +// ClearLists deletes all the entries of a list +func (o *Operator) ClearLists() { + o.Lock() + defer o.Unlock() + + log.Info("clearing domains lists: %d - %s", len(o.lists), o.Data) + for k := range o.lists { + delete(o.lists, k) + } + debug.FreeOSMemory() +} + +// StopMonitoringLists stops the monitoring lists goroutine. +func (o *Operator) StopMonitoringLists() { + if o.listsMonitorRunning == true { + o.exitMonitorChan <- true + o.exitMonitorChan = nil + o.listsMonitorRunning = false + } +} + +func (o *Operator) readDomainsList(raw, fileName string) (dups uint64) { + log.Debug("Loading domains list: %s, size: %d", fileName, len(raw)) + lines := strings.Split(string(raw), "\n") + for _, domain := range lines { + if len(domain) < 9 { + continue + } + // exclude not valid lines + if domain[:7] != "0.0.0.0" && domain[:9] != "127.0.0.1" { + continue + } + host := domain[8:] + // exclude localhost entries + if domain[:9] == "127.0.0.1" { + host = domain[10:] + } + if host == "local" || host == "localhost" || host == "localhost.localdomain" || host == "broadcasthost" { + continue + } + + host = core.Trim(host) + if _, found := o.lists[host]; found { + dups++ + continue + } + o.lists[host] = fileName + } + lines = nil + log.Info("%d domains loaded, %s", len(o.lists), fileName) + + return dups +} + +func (o *Operator) readNetList(raw, fileName string) (dups uint64) { + log.Debug("Loading nets list: %s, size: %d", fileName, len(raw)) + lines := strings.Split(string(raw), "\n") + for _, line := range lines { + if line == "" || line[0] == '#' { + continue + } + host := core.Trim(line) + if _, found := o.lists[host]; found { + dups++ + continue + } + _, netMask, err := net.ParseCIDR(host) + if err != nil { + log.Warning("Error parsing net from list: %s, (%s)", err, fileName) + continue + } + o.lists[host] = netMask + } + lines = nil + log.Info("%d nets loaded, %s", len(o.lists), fileName) + + return dups +} + +func (o *Operator) readRegexpList(raw, fileName string) (dups uint64) { + log.Debug("Loading regexp list: %s, size: %d", fileName, len(raw)) + lines := strings.Split(string(raw), "\n") + for n, line := range lines { + if line == "" || line[0] == '#' { + continue + } + host := core.Trim(line) + if _, found := o.lists[host]; found { + dups++ + continue + } + re, err := regexp.Compile(line) + if err != nil { + log.Warning("Error compiling regexp from list: %s, (%d:%s)", err, n, fileName) + continue + } + o.lists[line] = re + } + lines = nil + log.Info("%d regexps loaded, %s", len(o.lists), fileName) + + return dups +} + +func (o *Operator) readIPList(raw, fileName string) (dups uint64) { + log.Debug("Loading IPs list: %s, size: %d", fileName, len(raw)) + lines := strings.Split(string(raw), "\n") + for _, line := range lines { + if line == "" || line[0] == '#' { + continue + } + ip := core.Trim(line) + if _, found := o.lists[ip]; found { + dups++ + continue + } + o.lists[ip] = fileName + } + lines = nil + log.Info("%d IPs loaded, %s", len(o.lists), fileName) + + return dups +} + +func (o *Operator) readLists() error { + o.ClearLists() + + var dups uint64 + // this list is particular to this operator and rule + o.Lock() + defer o.Unlock() + o.lists = make(map[string]interface{}) + + expr := filepath.Join(o.Data, "*.*") + fileList, err := filepath.Glob(expr) + if err != nil { + return fmt.Errorf("Error loading domains lists '%s': %s", expr, err) + } + + for _, fileName := range fileList { + // ignore hidden files + name := filepath.Base(fileName) + if name[:1] == "." { + continue + } + + raw, err := ioutil.ReadFile(fileName) + if err != nil { + log.Warning("Error reading list of IPs (%s): %s", fileName, err) + continue + } + + if o.Operand == OpDomainsLists { + dups += o.readDomainsList(string(raw), fileName) + } else if o.Operand == OpDomainsRegexpLists { + dups += o.readRegexpList(string(raw), fileName) + } else if o.Operand == OpNetLists { + dups += o.readNetList(string(raw), fileName) + } else if o.Operand == OpIPLists { + dups += o.readIPList(string(raw), fileName) + } else { + log.Warning("Unknown lists operand type: %s", o.Operand) + } + } + log.Info("%d lists loaded, %d domains, %d duplicated", len(fileList), len(o.lists), dups) + return nil +} + +func (o *Operator) loadLists() { + log.Info("loading domains lists: %s, %s, %s", o.Type, o.Operand, o.Data) + + // when loading from disk, we don't use the Operator's constructor, so we need to create this channel + if o.exitMonitorChan == nil { + o.exitMonitorChan = make(chan bool) + o.listsMonitorRunning = true + go o.monitorLists() + } +} diff --git a/daemon/rule/operator_test.go b/daemon/rule/operator_test.go new file mode 100644 index 0000000..5d7d07b --- /dev/null +++ b/daemon/rule/operator_test.go @@ -0,0 +1,742 @@ +package rule + +import ( + "encoding/json" + "fmt" + "net" + "testing" + "time" + + "github.com/evilsocket/opensnitch/daemon/conman" + "github.com/evilsocket/opensnitch/daemon/core" + "github.com/evilsocket/opensnitch/daemon/netstat" + "github.com/evilsocket/opensnitch/daemon/procmon" +) + +var ( + defaultProcPath = "/usr/bin/opensnitchd" + defaultProcArgs = "-rules-path /etc/opensnitchd/rules/" + defaultDstHost = "opensnitch.io" + defaultDstPort = uint(443) + defaultDstIP = "185.53.178.14" + defaultUserID = 666 + + netEntry = &netstat.Entry{ + UserId: defaultUserID, + } + + proc = &procmon.Process{ + ID: 12345, + Path: defaultProcPath, + Args: []string{"-rules-path", "/etc/opensnitchd/rules/"}, + } + + conn = &conman.Connection{ + Protocol: "TCP", + SrcPort: 66666, + SrcIP: net.ParseIP("192.168.1.111"), + DstIP: net.ParseIP(defaultDstIP), + DstPort: defaultDstPort, + DstHost: defaultDstHost, + Process: proc, + Entry: netEntry, + } +) + +func compileListOperators(list *[]Operator, t *testing.T) { + op := *list + for i := 0; i < len(*list); i++ { + if err := op[i].Compile(); err != nil { + t.Error("NewOperator List, Compile() subitem error:", err) + } + } +} + +func unmarshalListData(data string, t *testing.T) (op *[]Operator) { + if err := json.Unmarshal([]byte(data), &op); err != nil { + t.Error("Error unmarshalling list data:", err, data) + return nil + } + return op +} + +func restoreConnection() { + conn.Process.Path = defaultProcPath + conn.DstHost = defaultDstHost + conn.DstPort = defaultDstPort + conn.Entry.UserId = defaultUserID +} + +func TestNewOperatorSimple(t *testing.T) { + t.Log("Test NewOperator() simple") + var list []Operator + + opSimple, err := NewOperator(Simple, false, OpTrue, "", list) + if err != nil { + t.Error("NewOperator simple.err should be nil: ", err) + t.Fail() + } + if err = opSimple.Compile(); err != nil { + t.Fail() + } + if opSimple.Match(nil) == false { + t.Error("Test NewOperator() simple.case-insensitive doesn't match") + t.Fail() + } + + t.Run("Operator Simple proc.id", func(t *testing.T) { + // proc.id not sensitive + opSimple, err = NewOperator(Simple, false, OpProcessID, "12345", list) + if err != nil { + t.Error("NewOperator simple.case-insensitive.proc.id err should be nil: ", err) + t.Fail() + } + if err = opSimple.Compile(); err != nil { + t.Error("NewOperator simple.case-insensitive.proc.id Compile() err:", err) + t.Fail() + } + if opSimple.Match(conn) == false { + t.Error("Test NewOperator() simple proc.id doesn't match") + t.Fail() + } + }) + + opSimple, err = NewOperator(Simple, false, OpProcessPath, defaultProcPath, list) + t.Run("Operator Simple proc.path case-insensitive", func(t *testing.T) { + // proc path not sensitive + if err != nil { + t.Error("NewOperator simple proc.path err should be nil: ", err) + t.Fail() + } + if err = opSimple.Compile(); err != nil { + t.Error("NewOperator simple.case-insensitive.proc.path Compile() err:", err) + t.Fail() + } + if opSimple.Match(conn) == false { + t.Error("Test NewOperator() simple proc.path doesn't match") + t.Fail() + } + }) + + t.Run("Operator Simple proc.path sensitive", func(t *testing.T) { + // proc path sensitive + opSimple.Sensitive = true + conn.Process.Path = "/usr/bin/OpenSnitchd" + if opSimple.Match(conn) == true { + t.Error("Test NewOperator() simple proc.path sensitive match") + t.Fail() + } + }) + + opSimple, err = NewOperator(Simple, false, OpDstHost, defaultDstHost, list) + t.Run("Operator Simple con.dstHost case-insensitive", func(t *testing.T) { + // proc dst host not sensitive + if err != nil { + t.Error("NewOperator simple proc.path err should be nil: ", err) + t.Fail() + } + if err = opSimple.Compile(); err != nil { + t.Error("NewOperator simple.case-insensitive.dstHost Compile() err:", err) + t.Fail() + } + if opSimple.Match(conn) == false { + t.Error("Test NewOperator() simple.conn.dstHost.not-sensitive doesn't match") + t.Fail() + } + }) + + t.Run("Operator Simple con.dstHost case-insensitive different host", func(t *testing.T) { + conn.DstHost = "www.opensnitch.io" + if opSimple.Match(conn) == true { + t.Error("Test NewOperator() simple.conn.dstHost.not-sensitive doesn't MATCH") + t.Fail() + } + }) + + t.Run("Operator Simple con.dstHost sensitive", func(t *testing.T) { + // proc dst host sensitive + opSimple, err = NewOperator(Simple, true, OpDstHost, "OpEnsNitCh.io", list) + if err != nil { + t.Error("NewOperator simple.dstHost.sensitive err should be nil: ", err) + t.Fail() + } + if err = opSimple.Compile(); err != nil { + t.Error("NewOperator simple.dstHost.sensitive Compile() err:", err) + t.Fail() + } + conn.DstHost = "OpEnsNitCh.io" + if opSimple.Match(conn) == false { + t.Error("Test NewOperator() simple.dstHost.sensitive doesn't match") + t.Fail() + } + }) + + t.Run("Operator Simple proc.args case-insensitive", func(t *testing.T) { + // proc args case-insensitive + opSimple, err = NewOperator(Simple, false, OpProcessCmd, defaultProcArgs, list) + if err != nil { + t.Error("NewOperator simple proc.args err should be nil: ", err) + t.Fail() + } + if err = opSimple.Compile(); err != nil { + t.Error("NewOperator simple proc.args Compile() err: ", err) + t.Fail() + } + if opSimple.Match(conn) == false { + t.Error("Test NewOperator() simple proc.args doesn't match") + t.Fail() + } + }) + + t.Run("Operator Simple con.dstIp case-insensitive", func(t *testing.T) { + // proc dstIp case-insensitive + opSimple, err = NewOperator(Simple, false, OpDstIP, defaultDstIP, list) + if err != nil { + t.Error("NewOperator simple conn.dstip.err should be nil: ", err) + t.Fail() + } + if err = opSimple.Compile(); err != nil { + t.Error("NewOperator simple con.dstIp Compile() err: ", err) + t.Fail() + } + if opSimple.Match(conn) == false { + t.Error("Test NewOperator() simple conn.dstip doesn't match") + t.Fail() + } + }) + + t.Run("Operator Simple UserId case-insensitive", func(t *testing.T) { + // conn.uid case-insensitive + opSimple, err = NewOperator(Simple, false, OpUserID, fmt.Sprint(defaultUserID), list) + if err != nil { + t.Error("NewOperator simple conn.userid.err should be nil: ", err) + t.Fail() + } + if err = opSimple.Compile(); err != nil { + t.Error("NewOperator simple UserId Compile() err: ", err) + t.Fail() + } + if opSimple.Match(conn) == false { + t.Error("Test NewOperator() simple conn.userid doesn't match") + t.Fail() + } + }) + + restoreConnection() +} + +func TestNewOperatorNetwork(t *testing.T) { + t.Log("Test NewOperator() network") + var dummyList []Operator + + opSimple, err := NewOperator(Network, false, OpDstNetwork, "185.53.178.14/24", dummyList) + if err != nil { + t.Error("NewOperator network.err should be nil: ", err) + t.Fail() + } + if err = opSimple.Compile(); err != nil { + t.Fail() + } + if opSimple.Match(conn) == false { + t.Error("Test NewOperator() network doesn't match") + t.Fail() + } + + opSimple, err = NewOperator(Network, false, OpDstNetwork, "8.8.8.8/24", dummyList) + if err != nil { + t.Error("NewOperator network.err should be nil: ", err) + t.Fail() + } + if err = opSimple.Compile(); err != nil { + t.Fail() + } + if opSimple.Match(conn) == true { + t.Error("Test NewOperator() network doesn't match:", conn.DstIP) + t.Fail() + } + + restoreConnection() +} + +func TestNewOperatorRegexp(t *testing.T) { + t.Log("Test NewOperator() regexp") + var dummyList []Operator + + opRE, err := NewOperator(Regexp, false, OpProto, "^TCP$", dummyList) + if err != nil { + t.Error("NewOperator regexp.err should be nil: ", err) + t.Fail() + } + if err = opRE.Compile(); err != nil { + t.Fail() + } + if opRE.Match(conn) == false { + t.Error("Test NewOperator() regexp doesn't match") + t.Fail() + } + + restoreConnection() +} + +func TestNewOperatorInvalidRegexp(t *testing.T) { + t.Log("Test NewOperator() invalid regexp") + var dummyList []Operator + + opRE, err := NewOperator(Regexp, false, OpProto, "^TC(P$", dummyList) + if err != nil { + t.Error("NewOperator regexp.err should be nil: ", err) + t.Fail() + } + if err = opRE.Compile(); err == nil { + t.Error("NewOperator() invalid regexp. It should fail: ", err) + t.Fail() + } + + restoreConnection() +} + +func TestNewOperatorRegexpSensitive(t *testing.T) { + t.Log("Test NewOperator() regexp sensitive") + var dummyList []Operator + + var sensitive Sensitive + sensitive = true + + conn.Process.Path = "/tmp/cUrL" + + opRE, err := NewOperator(Regexp, sensitive, OpProcessPath, "^/tmp/cUrL$", dummyList) + if err != nil { + t.Error("NewOperator regexp.case-sensitive.err should be nil: ", err) + t.Fail() + } + if err = opRE.Compile(); err != nil { + t.Fail() + } + if opRE.Match(conn) == false { + t.Error("Test NewOperator() RE sensitive doesn't match:", conn.Process.Path) + t.Fail() + } + + t.Run("Operator regexp proc.path case-sensitive", func(t *testing.T) { + conn.Process.Path = "/tmp/curl" + if opRE.Match(conn) == true { + t.Error("Test NewOperator() RE sensitive match:", conn.Process.Path) + t.Fail() + } + }) + + opRE, err = NewOperator(Regexp, !sensitive, OpProcessPath, "^/tmp/cUrL$", dummyList) + if err != nil { + t.Error("NewOperator regexp.case-insensitive.err should be nil: ", err) + t.Fail() + } + if err = opRE.Compile(); err != nil { + t.Fail() + } + if opRE.Match(conn) == false { + t.Error("Test NewOperator() RE not sensitive match:", conn.Process.Path) + t.Fail() + } + + restoreConnection() +} + +func TestNewOperatorList(t *testing.T) { + t.Log("Test NewOperator() List") + var list []Operator + listData := `[{"type": "simple", "operand": "dest.ip", "data": "185.53.178.14", "sensitive": false}, {"type": "simple", "operand": "dest.port", "data": "443", "sensitive": false}]` + + // simple list + opList, err := NewOperator(List, false, OpProto, listData, list) + t.Run("Operator List simple case-insensitive", func(t *testing.T) { + if err != nil { + t.Error("NewOperator list.regexp.err should be nil: ", err) + t.Fail() + } + if err = opList.Compile(); err != nil { + t.Fail() + } + opList.List = *unmarshalListData(opList.Data, t) + compileListOperators(&opList.List, t) + if opList.Match(conn) == false { + t.Error("Test NewOperator() list simple doesn't match") + t.Fail() + } + }) + + t.Run("Operator List regexp case-insensitive", func(t *testing.T) { + // list with regexp, case-insensitive + listData = `[{"type": "regexp", "operand": "process.path", "data": "^/usr/bin/.*", "sensitive": false},{"type": "simple", "operand": "dest.ip", "data": "185.53.178.14", "sensitive": false}, {"type": "simple", "operand": "dest.port", "data": "443", "sensitive": false}]` + opList.List = *unmarshalListData(listData, t) + compileListOperators(&opList.List, t) + if err = opList.Compile(); err != nil { + t.Fail() + } + if opList.Match(conn) == false { + t.Error("Test NewOperator() list regexp doesn't match") + t.Fail() + } + }) + + t.Run("Operator List regexp case-sensitive", func(t *testing.T) { + // list with regexp, case-sensitive + // "data": "^/usr/BiN/.*" must match conn.Process.Path (sensitive) + listData = `[{"type": "regexp", "operand": "process.path", "data": "^/usr/BiN/.*", "sensitive": false},{"type": "simple", "operand": "dest.ip", "data": "185.53.178.14", "sensitive": false}, {"type": "simple", "operand": "dest.port", "data": "443", "sensitive": false}]` + opList.List = *unmarshalListData(listData, t) + compileListOperators(&opList.List, t) + conn.Process.Path = "/usr/BiN/opensnitchd" + opList.Sensitive = true + if err = opList.Compile(); err != nil { + t.Fail() + } + if opList.Match(conn) == false { + t.Error("Test NewOperator() list.regexp.sensitive doesn't match:", conn.Process.Path) + t.Fail() + } + }) + + t.Run("Operator List regexp case-insensitive 2", func(t *testing.T) { + // "data": "^/usr/BiN/.*" must not match conn.Process.Path (insensitive) + opList.Sensitive = false + conn.Process.Path = "/USR/BiN/opensnitchd" + if err = opList.Compile(); err != nil { + t.Fail() + } + if opList.Match(conn) == false { + t.Error("Test NewOperator() list.regexp.insensitive match:", conn.Process.Path) + t.Fail() + } + }) + + t.Run("Operator List regexp case-insensitive 3", func(t *testing.T) { + // "data": "^/usr/BiN/.*" must match conn.Process.Path (insensitive) + opList.Sensitive = false + conn.Process.Path = "/USR/bin/opensnitchd" + if err = opList.Compile(); err != nil { + t.Fail() + } + if opList.Match(conn) == false { + t.Error("Test NewOperator() list.regexp.insensitive match:", conn.Process.Path) + t.Fail() + } + }) + + restoreConnection() +} + +func TestNewOperatorListsSimple(t *testing.T) { + t.Log("Test NewOperator() Lists simple") + var dummyList []Operator + + opLists, err := NewOperator(Lists, false, OpDomainsLists, "testdata/lists/domains/", dummyList) + if err != nil { + t.Error("NewOperator Lists, shouldn't be nil: ", err) + t.Fail() + } + if err = opLists.Compile(); err != nil { + t.Error("NewOperator Lists, Compile() error:", err) + } + time.Sleep(time.Second) + t.Log("testing Lists, DstHost:", conn.DstHost) + // The list contains 4 lines, 1 is a comment and there's a domain duplicated. + // We should only load lines that start with 0.0.0.0 or 127.0.0.1 + if len(opLists.lists) != 2 { + t.Error("NewOperator Lists, number of domains error:", opLists.lists, len(opLists.lists)) + } + if opLists.Match(conn) == false { + t.Error("Test NewOperator() lists doesn't match") + } + + opLists.StopMonitoringLists() + time.Sleep(time.Second) + opLists.Lock() + if len(opLists.lists) != 0 { + t.Error("NewOperator Lists, number should be 0 after stop:", opLists.lists, len(opLists.lists)) + } + opLists.Unlock() + + restoreConnection() +} + +func TestNewOperatorListsIPs(t *testing.T) { + t.Log("Test NewOperator() Lists domains_regexp") + + var subOp *Operator + var list []Operator + listData := `[{"type": "simple", "operand": "user.id", "data": "666", "sensitive": false}, {"type": "lists", "operand": "lists.ips", "data": "testdata/lists/ips/", "sensitive": false}]` + + opLists, err := NewOperator(List, false, OpList, listData, list) + if err != nil { + t.Error("NewOperator Lists domains_regexp, shouldn't be nil: ", err) + t.Fail() + } + if err := opLists.Compile(); err != nil { + t.Error("NewOperator Lists domains_regexp, Compile() error:", err) + } + opLists.List = *unmarshalListData(opLists.Data, t) + for i := 0; i < len(opLists.List); i++ { + if err := opLists.List[i].Compile(); err != nil { + t.Error("NewOperator Lists domains_regexp, Compile() subitem error:", err) + } + if opLists.List[i].Type == Lists { + subOp = &opLists.List[i] + } + } + + time.Sleep(time.Second) + if opLists.Match(conn) == false { + t.Error("Test NewOperator() Lists domains_regexp, doesn't match:", conn.DstHost) + } + + subOp.Lock() + listslen := len(subOp.lists) + subOp.Unlock() + if listslen != 2 { + t.Error("NewOperator Lists domains_regexp, number of domains error:", subOp.lists) + } + + //t.Log("checking lists.domains_regexp:", tries, conn.DstHost) + if opLists.Match(conn) == false { + // we don't care about if it matches, we're testing race conditions + t.Log("Test NewOperator() Lists domains_regexp, doesn't match:", conn.DstHost) + } + + subOp.StopMonitoringLists() + time.Sleep(time.Second) + subOp.Lock() + if len(subOp.lists) != 0 { + t.Error("NewOperator Lists number should be 0:", subOp.lists, len(subOp.lists)) + } + subOp.Unlock() + + restoreConnection() +} + +func TestNewOperatorListsNETs(t *testing.T) { + t.Log("Test NewOperator() Lists domains_regexp") + + var subOp *Operator + var list []Operator + listData := `[{"type": "simple", "operand": "user.id", "data": "666", "sensitive": false}, {"type": "lists", "operand": "lists.nets", "data": "testdata/lists/nets/", "sensitive": false}]` + + opLists, err := NewOperator(List, false, OpList, listData, list) + if err != nil { + t.Error("NewOperator Lists domains_regexp, shouldn't be nil: ", err) + t.Fail() + } + if err := opLists.Compile(); err != nil { + t.Error("NewOperator Lists domains_regexp, Compile() error:", err) + } + opLists.List = *unmarshalListData(opLists.Data, t) + for i := 0; i < len(opLists.List); i++ { + if err := opLists.List[i].Compile(); err != nil { + t.Error("NewOperator Lists domains_regexp, Compile() subitem error:", err) + } + if opLists.List[i].Type == Lists { + subOp = &opLists.List[i] + } + } + + time.Sleep(time.Second) + if opLists.Match(conn) == false { + t.Error("Test NewOperator() Lists domains_regexp, doesn't match:", conn.DstHost) + } + + subOp.Lock() + listslen := len(subOp.lists) + subOp.Unlock() + if listslen != 2 { + t.Error("NewOperator Lists domains_regexp, number of domains error:", subOp.lists) + } + + //t.Log("checking lists.domains_regexp:", tries, conn.DstHost) + if opLists.Match(conn) == false { + // we don't care about if it matches, we're testing race conditions + t.Log("Test NewOperator() Lists domains_regexp, doesn't match:", conn.DstHost) + } + + subOp.StopMonitoringLists() + time.Sleep(time.Second) + subOp.Lock() + if len(subOp.lists) != 0 { + t.Error("NewOperator Lists number should be 0:", subOp.lists, len(subOp.lists)) + } + subOp.Unlock() + + restoreConnection() +} + +func TestNewOperatorListsComplex(t *testing.T) { + t.Log("Test NewOperator() Lists complex") + var subOp *Operator + var list []Operator + listData := `[{"type": "simple", "operand": "user.id", "data": "666", "sensitive": false}, {"type": "lists", "operand": "lists.domains", "data": "testdata/lists/domains/", "sensitive": false}]` + + opLists, err := NewOperator(List, false, OpList, listData, list) + if err != nil { + t.Error("NewOperator Lists complex, shouldn't be nil: ", err) + t.Fail() + } + if err := opLists.Compile(); err != nil { + t.Error("NewOperator Lists complex, Compile() error:", err) + } + opLists.List = *unmarshalListData(opLists.Data, t) + for i := 0; i < len(opLists.List); i++ { + if err := opLists.List[i].Compile(); err != nil { + t.Error("NewOperator Lists complex, Compile() subitem error:", err) + } + if opLists.List[i].Type == Lists { + subOp = &opLists.List[i] + } + } + time.Sleep(time.Second) + subOp.Lock() + if len(subOp.lists) != 2 { + t.Error("NewOperator Lists complex, number of domains error:", subOp.lists) + } + subOp.Unlock() + if opLists.Match(conn) == false { + t.Error("Test NewOperator() Lists complex, doesn't match") + } + + subOp.StopMonitoringLists() + time.Sleep(time.Second) + subOp.Lock() + if len(subOp.lists) != 0 { + t.Error("NewOperator Lists number should be 0:", subOp.lists, len(subOp.lists)) + } + subOp.Unlock() + + restoreConnection() +} + +func TestNewOperatorListsDomainsRegexp(t *testing.T) { + t.Log("Test NewOperator() Lists domains_regexp") + + var subOp *Operator + var list []Operator + listData := `[{"type": "simple", "operand": "user.id", "data": "666", "sensitive": false}, {"type": "lists", "operand": "lists.domains_regexp", "data": "testdata/lists/regexp/", "sensitive": false}]` + + opLists, err := NewOperator(List, false, OpList, listData, list) + if err != nil { + t.Error("NewOperator Lists domains_regexp, shouldn't be nil: ", err) + t.Fail() + } + if err := opLists.Compile(); err != nil { + t.Error("NewOperator Lists domains_regexp, Compile() error:", err) + } + opLists.List = *unmarshalListData(opLists.Data, t) + for i := 0; i < len(opLists.List); i++ { + if err := opLists.List[i].Compile(); err != nil { + t.Error("NewOperator Lists domains_regexp, Compile() subitem error:", err) + } + if opLists.List[i].Type == Lists { + subOp = &opLists.List[i] + } + } + + time.Sleep(time.Second) + if opLists.Match(conn) == false { + t.Error("Test NewOperator() Lists domains_regexp, doesn't match:", conn.DstHost) + } + + subOp.Lock() + listslen := len(subOp.lists) + subOp.Unlock() + if listslen != 2 { + t.Error("NewOperator Lists domains_regexp, number of domains error:", subOp.lists) + } + + //t.Log("checking lists.domains_regexp:", tries, conn.DstHost) + if opLists.Match(conn) == false { + // we don't care about if it matches, we're testing race conditions + t.Log("Test NewOperator() Lists domains_regexp, doesn't match:", conn.DstHost) + } + + subOp.StopMonitoringLists() + time.Sleep(time.Second) + subOp.Lock() + if len(subOp.lists) != 0 { + t.Error("NewOperator Lists number should be 0:", subOp.lists, len(subOp.lists)) + } + subOp.Unlock() + + restoreConnection() +} + +// Must be launched with -race to test that we don't cause leaks +// Race occured on operator.go:241 reListCmp().MathString() +// fixed here: 53419fe +func TestRaceNewOperatorListsDomainsRegexp(t *testing.T) { + t.Log("Test NewOperator() Lists domains_regexp") + + var subOp *Operator + var list []Operator + listData := `[{"type": "simple", "operand": "user.id", "data": "666", "sensitive": false}, {"type": "lists", "operand": "lists.domains_regexp", "data": "testdata/lists/regexp/", "sensitive": false}]` + + opLists, err := NewOperator(List, false, OpList, listData, list) + if err != nil { + t.Error("NewOperator Lists domains_regexp, shouldn't be nil: ", err) + t.Fail() + } + if err := opLists.Compile(); err != nil { + t.Error("NewOperator Lists domains_regexp, Compile() error:", err) + } + opLists.List = *unmarshalListData(opLists.Data, t) + for i := 0; i < len(opLists.List); i++ { + if err := opLists.List[i].Compile(); err != nil { + t.Error("NewOperator Lists domains_regexp, Compile() subitem error:", err) + } + if opLists.List[i].Type == Lists { + subOp = &opLists.List[i] + } + } + + // touch domains list in background, to force a reload. + go func() { + touches := 1000 + for { + if touches < 0 { + break + } + core.Exec("/bin/touch", []string{"testdata/lists/regexp/domainsregexp.txt"}) + touches-- + time.Sleep(100 * time.Millisecond) + //t.Log("touching:", touches) + } + }() + + time.Sleep(time.Second) + + subOp.Lock() + listslen := len(subOp.lists) + subOp.Unlock() + if listslen != 2 { + t.Error("NewOperator Lists domains_regexp, number of domains error:", subOp.lists) + } + + tries := 10000 + for { + if tries < 0 { + break + } + //t.Log("checking lists.domains_regexp:", tries, conn.DstHost) + if opLists.Match(conn) == false { + // we don't care about if it matches, we're testing race conditions + t.Log("Test NewOperator() Lists domains_regexp, doesn't match:", conn.DstHost) + } + + tries-- + time.Sleep(10 * time.Millisecond) + } + + subOp.StopMonitoringLists() + time.Sleep(time.Second) + subOp.Lock() + if len(subOp.lists) != 0 { + t.Error("NewOperator Lists number should be 0:", subOp.lists, len(subOp.lists)) + } + subOp.Unlock() + + restoreConnection() +} diff --git a/daemon/rule/rule.go b/daemon/rule/rule.go new file mode 100644 index 0000000..794f27c --- /dev/null +++ b/daemon/rule/rule.go @@ -0,0 +1,170 @@ +package rule + +import ( + "fmt" + "time" + + "github.com/evilsocket/opensnitch/daemon/conman" + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/evilsocket/opensnitch/daemon/ui/protocol" +) + +// Action of a rule +type Action string + +// Actions of rules +const ( + Allow = Action("allow") + Deny = Action("deny") + Reject = Action("reject") +) + +// Duration of a rule +type Duration string + +// daemon possible durations +const ( + Once = Duration("once") + Restart = Duration("until restart") + Always = Duration("always") +) + +// Rule represents an action on a connection. +// The fields match the ones saved as json to disk. +// If a .json rule file is modified on disk, it's reloaded automatically. +type Rule struct { + // Save date fields as string, to avoid issues marshalling Time (#1140). + Created string `json:"created"` + Updated string `json:"updated"` + + Name string `json:"name"` + Description string `json:"description"` + Action Action `json:"action"` + Duration Duration `json:"duration"` + Operator Operator `json:"operator"` + Enabled bool `json:"enabled"` + Precedence bool `json:"precedence"` + Nolog bool `json:"nolog"` +} + +// Create creates a new rule object with the specified parameters. +func Create(name, description string, enabled, precedence, nolog bool, action Action, duration Duration, op *Operator) *Rule { + return &Rule{ + Created: time.Now().Format(time.RFC3339), + Enabled: enabled, + Precedence: precedence, + Nolog: nolog, + Name: name, + Description: description, + Action: action, + Duration: duration, + Operator: *op, + } +} + +func (r *Rule) String() string { + enabled := "Disabled" + if r.Enabled { + enabled = "Enabled" + } + return fmt.Sprintf("[%s] %s: if(%s){ %s %s }", enabled, r.Name, r.Operator.String(), r.Action, r.Duration) +} + +// Match performs on a connection the checks a Rule has, to determine if it +// must be allowed or denied. +func (r *Rule) Match(con *conman.Connection) bool { + return r.Operator.Match(con) +} + +// Deserialize translates back the rule received to a Rule object +func Deserialize(reply *protocol.Rule) (*Rule, error) { + if reply.Operator == nil { + log.Warning("Deserialize rule, Operator nil") + return nil, fmt.Errorf("invalid operator") + } + operator, err := NewOperator( + Type(reply.Operator.Type), + Sensitive(reply.Operator.Sensitive), + Operand(reply.Operator.Operand), + reply.Operator.Data, + make([]Operator, 0), + ) + if err != nil { + log.Warning("Deserialize rule, NewOperator() error: %s", err) + return nil, err + } + + newRule := Create( + reply.Name, + reply.Description, + reply.Enabled, + reply.Precedence, + reply.Nolog, + Action(reply.Action), + Duration(reply.Duration), + operator, + ) + + if Type(reply.Operator.Type) == List { + newRule.Operator.Data = "" + reply.Operator.Data = "" + for i := 0; i < len(reply.Operator.List); i++ { + newRule.Operator.List = append( + newRule.Operator.List, + Operator{ + Type: Type(reply.Operator.List[i].Type), + Sensitive: Sensitive(reply.Operator.List[i].Sensitive), + Operand: Operand(reply.Operator.List[i].Operand), + Data: string(reply.Operator.List[i].Data), + }, + ) + } + } + + return newRule, nil +} + +// Serialize translates a Rule to the protocol object +func (r *Rule) Serialize() *protocol.Rule { + if r == nil { + return nil + } + + created, err := time.Parse(time.RFC3339, r.Created) + if err != nil { + log.Warning("Error parsing rule Created date (it should be in RFC3339 format): %s (%s)", err, string(r.Name)) + log.Warning("using current time instead: %s", created) + created = time.Now() + } + + protoRule := &protocol.Rule{ + Created: created.Unix(), + Name: string(r.Name), + Description: string(r.Description), + Enabled: bool(r.Enabled), + Precedence: bool(r.Precedence), + Nolog: bool(r.Nolog), + Action: string(r.Action), + Duration: string(r.Duration), + Operator: &protocol.Operator{ + Type: string(r.Operator.Type), + Sensitive: bool(r.Operator.Sensitive), + Operand: string(r.Operator.Operand), + Data: string(r.Operator.Data), + }, + } + if r.Operator.Type == List { + r.Operator.Data = "" + for i := 0; i < len(r.Operator.List); i++ { + protoRule.Operator.List = append(protoRule.Operator.List, + &protocol.Operator{ + Type: string(r.Operator.List[i].Type), + Sensitive: bool(r.Operator.List[i].Sensitive), + Operand: string(r.Operator.List[i].Operand), + Data: string(r.Operator.List[i].Data), + }) + } + } + + return protoRule +} diff --git a/daemon/rule/rule_test.go b/daemon/rule/rule_test.go new file mode 100644 index 0000000..bde6f19 --- /dev/null +++ b/daemon/rule/rule_test.go @@ -0,0 +1,119 @@ +package rule + +import ( + "testing" +) + +func TestCreate(t *testing.T) { + t.Log("Test: Create rule") + + var list []Operator + oper, _ := NewOperator(Simple, false, OpTrue, "", list) + r := Create("000-test-name", "rule description 000", true, false, false, Allow, Once, oper) + t.Run("New rule must not be nil", func(t *testing.T) { + if r == nil { + t.Error("Create() returned nil") + t.Fail() + } + }) + t.Run("Rule name must be 000-test-name", func(t *testing.T) { + if r.Name != "000-test-name" { + t.Error("Rule name error:", r.Name) + t.Fail() + } + }) + t.Run("Rule must be enabled", func(t *testing.T) { + if r.Enabled == false { + t.Error("Rule Enabled is false:", r) + t.Fail() + } + }) + t.Run("Rule Precedence must be false", func(t *testing.T) { + if r.Precedence == true { + t.Error("Rule Precedence is true:", r) + t.Fail() + } + }) + t.Run("Rule Action must be Allow", func(t *testing.T) { + if r.Action != Allow { + t.Error("Rule Action is not Allow:", r.Action) + t.Fail() + } + }) + t.Run("Rule Duration should be Once", func(t *testing.T) { + if r.Duration != Once { + t.Error("Rule Duration is not Once:", r.Duration) + t.Fail() + } + }) +} + +func TestRuleSerializers(t *testing.T) { + t.Log("Test: Serializers()") + + var opList []Operator + opList = append(opList, Operator{ + Type: Simple, + Operand: OpProcessPath, + Data: "/path/x", + }) + opList = append(opList, Operator{ + Type: Simple, + Operand: OpDstPort, + Data: "23", + }) + + op, _ := NewOperator(List, false, OpTrue, "", opList) + // this string must be erased after Deserialized + op.Data = "[\"test\": true]" + + r := Create("000-test-serializer-list", "rule description 000", true, false, false, Allow, Once, op) + + rSerialized := r.Serialize() + t.Run("Serialize() must not return nil", func(t *testing.T) { + if rSerialized == nil { + t.Error("rule.Serialize() returned nil") + t.Fail() + } + }) + + rDeser, err := Deserialize(rSerialized) + t.Run("Deserialize must not return error", func(t *testing.T) { + if err != nil { + t.Error("rule.Serialize() returned error:", err) + t.Fail() + } + }) + + // commit: b93051026e6a82ba07a5ac2f072880e69f04c238 + t.Run("Deserialize. Operator.Data must be empty", func(t *testing.T) { + if rDeser.Operator.Data != "" { + t.Error("rule.Deserialize() Operator.Data not emptied:", rDeser.Operator.Data) + t.Fail() + } + }) + + t.Run("Deserialize. Operator.List must be expanded", func(t *testing.T) { + if len(rDeser.Operator.List) != 2 { + t.Error("rule.Deserialize() invalid Operator.List:", rDeser.Operator.List) + t.Fail() + } + if rDeser.Operator.List[0].Operand != OpProcessPath { + t.Error("rule.Deserialize() invalid Operator.List 1:", rDeser.Operator.List) + t.Fail() + } + if rDeser.Operator.List[1].Operand != OpDstPort { + t.Error("rule.Deserialize() invalid Operator.List 2:", rDeser.Operator.List) + t.Fail() + } + if rDeser.Operator.List[0].Type != Simple || rDeser.Operator.List[1].Type != Simple { + t.Error("rule.Deserialize() invalid Operator.List 3:", rDeser.Operator.List) + t.Fail() + } + if rDeser.Operator.List[0].Data != "/path/x" || rDeser.Operator.List[1].Data != "23" { + t.Error("rule.Deserialize() invalid Operator.List 4:", rDeser.Operator.List) + t.Fail() + } + }) + +} diff --git a/daemon/rule/testdata/000-allow-chrome.json b/daemon/rule/testdata/000-allow-chrome.json new file mode 100644 index 0000000..db2c811 --- /dev/null +++ b/daemon/rule/testdata/000-allow-chrome.json @@ -0,0 +1,16 @@ +{ + "created": "2020-12-13T18:06:52.209804547+01:00", + "updated": "2020-12-13T18:06:52.209857713+01:00", + "name": "000-allow-chrome", + "enabled": true, + "precedence": true, + "action": "allow", + "duration": "always", + "operator": { + "type": "simple", + "operand": "process.path", + "sensitive": false, + "data": "/opt/google/chrome/chrome", + "list": [] + } +} \ No newline at end of file diff --git a/daemon/rule/testdata/001-deny-chrome.json b/daemon/rule/testdata/001-deny-chrome.json new file mode 100644 index 0000000..27c266c --- /dev/null +++ b/daemon/rule/testdata/001-deny-chrome.json @@ -0,0 +1,16 @@ +{ + "created": "2020-12-13T17:54:49.067148304+01:00", + "updated": "2020-12-13T17:54:49.067213602+01:00", + "name": "001-deny-chrome", + "enabled": true, + "precedence": false, + "action": "deny", + "duration": "always", + "operator": { + "type": "simple", + "operand": "process.path", + "sensitive": false, + "data": "/opt/google/chrome/chrome", + "list": [] + } +} \ No newline at end of file diff --git a/daemon/rule/testdata/invalid-regexp-list.json b/daemon/rule/testdata/invalid-regexp-list.json new file mode 100644 index 0000000..bd8973f --- /dev/null +++ b/daemon/rule/testdata/invalid-regexp-list.json @@ -0,0 +1,31 @@ +{ + "created": "2020-12-13T18:06:52.209804547+01:00", + "updated": "2020-12-13T18:06:52.209857713+01:00", + "name": "invalid-regexp-list", + "enabled": true, + "precedence": true, + "action": "allow", + "duration": "always", + "operator": { + "type": "list", + "operand": "list", + "sensitive": false, + "data": "[{\"type\": \"regexp\", \"operand\": \"process.path\", \"sensitive\": false, \"data\": \"^(/di(rmngr$\"}, {\"type\": \"simple\", \"operand\": \"dest.port\", \"data\": \"53\", \"sensitive\": false}]", + "list": [ + { + "type": "regexp", + "operand": "process.path", + "sensitive": false, + "data": "^(/di(rmngr)$", + "list": null + }, + { + "type": "simple", + "operand": "dest.port", + "sensitive": false, + "data": "53", + "list": null + } + ] + } +} diff --git a/daemon/rule/testdata/invalid-regexp.json b/daemon/rule/testdata/invalid-regexp.json new file mode 100644 index 0000000..d296098 --- /dev/null +++ b/daemon/rule/testdata/invalid-regexp.json @@ -0,0 +1,16 @@ +{ + "created": "2020-12-13T18:06:52.209804547+01:00", + "updated": "2020-12-13T18:06:52.209857713+01:00", + "name": "invalid-regexp", + "enabled": true, + "precedence": true, + "action": "allow", + "duration": "always", + "operator": { + "type": "regexp", + "operand": "process.path", + "sensitive": false, + "data": "/opt/((.*)google/chrome/chrome", + "list": [] + } +} diff --git a/daemon/rule/testdata/lists/domains/domainlists.txt b/daemon/rule/testdata/lists/domains/domainlists.txt new file mode 100644 index 0000000..6e2f3e2 --- /dev/null +++ b/daemon/rule/testdata/lists/domains/domainlists.txt @@ -0,0 +1,4 @@ +# this line must be ignored, 0.0.0.0 www.test.org +0.0.0.0 www.test.org +127.0.0.1 www.test.org +0.0.0.0 opensnitch.io diff --git a/daemon/rule/testdata/lists/ips/ips.txt b/daemon/rule/testdata/lists/ips/ips.txt new file mode 100644 index 0000000..6514d30 --- /dev/null +++ b/daemon/rule/testdata/lists/ips/ips.txt @@ -0,0 +1,7 @@ +# this line must be ignored, 0.0.0.0 www.test.org + +# empty lines are also ignored +1.1.1.1 +185.53.178.14 +# duplicated entries should be ignored +1.1.1.1 diff --git a/daemon/rule/testdata/lists/nets/nets.txt b/daemon/rule/testdata/lists/nets/nets.txt new file mode 100644 index 0000000..8041c92 --- /dev/null +++ b/daemon/rule/testdata/lists/nets/nets.txt @@ -0,0 +1,8 @@ +# this line must be ignored, 0.0.0.0 www.test.org + +# empty lines are also ignored +1.1.1.0/24 +185.53.178.0/24 +# duplicated entries should be ignored +1.1.1.0/24 + diff --git a/daemon/rule/testdata/lists/regexp/domainsregexp.txt b/daemon/rule/testdata/lists/regexp/domainsregexp.txt new file mode 100644 index 0000000..85ab3e9 --- /dev/null +++ b/daemon/rule/testdata/lists/regexp/domainsregexp.txt @@ -0,0 +1,4 @@ +# this line must be ignored, 0.0.0.0 www.test.org +www.test.org +www.test.org +opensnitch.io diff --git a/daemon/rule/testdata/live_reload/test-live-reload-delete.json b/daemon/rule/testdata/live_reload/test-live-reload-delete.json new file mode 100644 index 0000000..5a4591a --- /dev/null +++ b/daemon/rule/testdata/live_reload/test-live-reload-delete.json @@ -0,0 +1,16 @@ +{ + "created": "2020-12-13T18:06:52.209804547+01:00", + "updated": "2020-12-13T18:06:52.209857713+01:00", + "name": "test-live-reload-delete", + "enabled": true, + "precedence": true, + "action": "deny", + "duration": "always", + "operator": { + "type": "simple", + "operand": "process.path", + "sensitive": false, + "data": "/usr/bin/curl", + "list": [] + } + } \ No newline at end of file diff --git a/daemon/rule/testdata/live_reload/test-live-reload-remove.json b/daemon/rule/testdata/live_reload/test-live-reload-remove.json new file mode 100644 index 0000000..8f21ed9 --- /dev/null +++ b/daemon/rule/testdata/live_reload/test-live-reload-remove.json @@ -0,0 +1,16 @@ +{ + "created": "2020-12-13T18:06:52.209804547+01:00", + "updated": "2020-12-13T18:06:52.209857713+01:00", + "name": "test-live-reload-remove", + "enabled": true, + "precedence": true, + "action": "deny", + "duration": "always", + "operator": { + "type": "simple", + "operand": "process.path", + "sensitive": false, + "data": "/usr/bin/curl", + "list": [] + } + } \ No newline at end of file diff --git a/daemon/rule/testdata/rule-disabled-operator-list-expanded.json b/daemon/rule/testdata/rule-disabled-operator-list-expanded.json new file mode 100644 index 0000000..2d6153f --- /dev/null +++ b/daemon/rule/testdata/rule-disabled-operator-list-expanded.json @@ -0,0 +1,31 @@ +{ + "created": "2023-10-03T18:06:52.209804547+01:00", + "updated": "2023-10-03T18:06:52.209857713+01:00", + "name": "rule-disabled-with-operators-list-expanded", + "enabled": false, + "precedence": true, + "action": "allow", + "duration": "always", + "operator": { + "type": "list", + "operand": "list", + "sensitive": false, + "data": "", + "list": [ + { + "type": "simple", + "operand": "process.path", + "sensitive": false, + "data": "/usr/bin/telnet", + "list": null + }, + { + "type": "simple", + "operand": "dest.port", + "sensitive": false, + "data": "53", + "list": null + } + ] + } +} diff --git a/daemon/rule/testdata/rule-disabled-operator-list.json b/daemon/rule/testdata/rule-disabled-operator-list.json new file mode 100644 index 0000000..29c8a7a --- /dev/null +++ b/daemon/rule/testdata/rule-disabled-operator-list.json @@ -0,0 +1,17 @@ +{ + "created": "2023-10-03T18:06:52.209804547+01:00", + "updated": "2023-10-03T18:06:52.209857713+01:00", + "name": "rule-disabled-with-operators-list-as-json-string", + "enabled": false, + "precedence": true, + "action": "allow", + "duration": "always", + "operator": { + "type": "list", + "operand": "list", + "sensitive": false, + "data": "[{\"type\": \"simple\", \"operand\": \"process.path\", \"sensitive\": false, \"data\": \"/usr/bin/telnet\"}, {\"type\": \"simple\", \"operand\": \"dest.port\", \"data\": \"53\", \"sensitive\": false}]", + "list": [ + ] + } +} diff --git a/daemon/rule/testdata/rule-operator-list-data-empty.json b/daemon/rule/testdata/rule-operator-list-data-empty.json new file mode 100644 index 0000000..76218c6 --- /dev/null +++ b/daemon/rule/testdata/rule-operator-list-data-empty.json @@ -0,0 +1,32 @@ +{ + "created": "2023-10-03T18:06:52.209804547+01:00", + "updated": "2023-10-03T18:06:52.209857713+01:00", + "name": "rule-with-operator-list-data-empty", + "enabled": true, + "precedence": true, + "action": "allow", + "duration": "always", + "operator": { + "type": "list", + "operand": "list", + "sensitive": false, + "data": "", + "list": [ + { + "type": "simple", + "operand": "process.path", + "sensitive": false, + "data": "/usr/bin/telnet", + "list": null + }, + { + "type": "simple", + "operand": "dest.port", + "sensitive": false, + "data": "53", + "list": null + } + ] + } +} + diff --git a/daemon/rule/testdata/rule-operator-list.json b/daemon/rule/testdata/rule-operator-list.json new file mode 100644 index 0000000..f4c46d8 --- /dev/null +++ b/daemon/rule/testdata/rule-operator-list.json @@ -0,0 +1,31 @@ +{ + "created": "2023-10-03T18:06:52.209804547+01:00", + "updated": "2023-10-03T18:06:52.209857713+01:00", + "name": "rule-with-operator-list", + "enabled": true, + "precedence": true, + "action": "allow", + "duration": "always", + "operator": { + "type": "list", + "operand": "list", + "sensitive": false, + "data": "[{\"type\": \"simple\", \"operand\": \"process.path\", \"sensitive\": false, \"data\": \"/usr/bin/telnet\"}, {\"type\": \"simple\", \"operand\": \"dest.port\", \"data\": \"53\", \"sensitive\": false}]", + "list": [ + { + "type": "simple", + "operand": "process.path", + "sensitive": false, + "data": "/usr/bin/telnet", + "list": null + }, + { + "type": "simple", + "operand": "dest.port", + "sensitive": false, + "data": "53", + "list": null + } + ] + } +} diff --git a/daemon/statistics/event.go b/daemon/statistics/event.go new file mode 100644 index 0000000..fe9e9ee --- /dev/null +++ b/daemon/statistics/event.go @@ -0,0 +1,32 @@ +package statistics + +import ( + "time" + + "github.com/evilsocket/opensnitch/daemon/conman" + "github.com/evilsocket/opensnitch/daemon/rule" + "github.com/evilsocket/opensnitch/daemon/ui/protocol" +) + +type Event struct { + Time time.Time + Connection *conman.Connection + Rule *rule.Rule +} + +func NewEvent(con *conman.Connection, match *rule.Rule) *Event { + return &Event{ + Time: time.Now(), + Connection: con, + Rule: match, + } +} + +func (e *Event) Serialize() *protocol.Event { + return &protocol.Event{ + Time: e.Time.Format("2006-01-02 15:04:05"), + Connection: e.Connection.Serialize(), + Rule: e.Rule.Serialize(), + Unixnano: e.Time.UnixNano(), + } +} diff --git a/daemon/statistics/stats.go b/daemon/statistics/stats.go new file mode 100644 index 0000000..68fd5de --- /dev/null +++ b/daemon/statistics/stats.go @@ -0,0 +1,263 @@ +package statistics + +import ( + "strconv" + "sync" + "time" + + "github.com/evilsocket/opensnitch/daemon/conman" + "github.com/evilsocket/opensnitch/daemon/core" + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/evilsocket/opensnitch/daemon/log/loggers" + "github.com/evilsocket/opensnitch/daemon/rule" + "github.com/evilsocket/opensnitch/daemon/ui/protocol" +) + +// StatsConfig holds the stats confguration +type StatsConfig struct { + MaxEvents int `json:"MaxEvents"` + MaxStats int `json:"MaxStats"` + Workers int `json:"Workers"` +} + +type conEvent struct { + con *conman.Connection + match *rule.Rule + wasMissed bool +} + +// Statistics holds the connections and statistics the daemon intercepts. +// The connections are stored in the Events slice. +type Statistics struct { + logger *loggers.LoggerManager + rules *rule.Loader + Started time.Time + ByExecutable map[string]uint64 + ByPort map[string]uint64 + ByProto map[string]uint64 + ByAddress map[string]uint64 + ByHost map[string]uint64 + jobs chan conEvent + ByUID map[string]uint64 + Events []*Event + Dropped int + // max number of events to keep in the buffer + maxEvents int + // max number of entries for each By* map + maxStats int + DNSResponses int + Connections int + Ignored int + Accepted int + RuleHits int + RuleMisses int + + sync.RWMutex +} + +// New returns a new Statistics object and initializes the go routines to update the stats. +func New(rules *rule.Loader) (stats *Statistics) { + stats = &Statistics{ + Started: time.Now(), + Events: make([]*Event, 0), + ByProto: make(map[string]uint64), + ByAddress: make(map[string]uint64), + ByHost: make(map[string]uint64), + ByPort: make(map[string]uint64), + ByUID: make(map[string]uint64), + ByExecutable: make(map[string]uint64), + + rules: rules, + jobs: make(chan conEvent), + maxEvents: 150, + maxStats: 25, + } + + return stats +} + +// SetLoggers sets the configured loggers where we'll write the events. +func (s *Statistics) SetLoggers(loggers *loggers.LoggerManager) { + s.logger = loggers +} + +// SetLimits configures the max events to keep in the backlog before sending +// the stats to the UI, or while the UI is not connected. +// if the backlog is full, it'll be shifted by one. +func (s *Statistics) SetLimits(config StatsConfig) { + if config.MaxEvents > 0 { + s.maxEvents = config.MaxEvents + } + if config.MaxStats > 0 { + s.maxStats = config.MaxStats + } + wrks := config.Workers + if wrks == 0 { + wrks = 6 + } + log.Info("Stats, max events: %d, max stats: %d, max workers: %d", s.maxStats, s.maxEvents, wrks) + for i := 0; i < wrks; i++ { + go s.eventWorker(i) + } + +} + +// OnConnectionEvent sends the details of a new connection throughout a channel, +// in order to add the connection to the stats. +func (s *Statistics) OnConnectionEvent(con *conman.Connection, match *rule.Rule, wasMissed bool) { + s.jobs <- conEvent{ + con: con, + match: match, + wasMissed: wasMissed, + } + action := "<nil>" + rname := "<nil>" + if match != nil { + action = string(match.Action) + rname = string(match.Name) + } + + s.logger.Log(con.Serialize(), action, rname) +} + +// OnDNSResponse increases the counter of dns and accepted connections. +func (s *Statistics) OnDNSResponse() { + s.Lock() + defer s.Unlock() + s.DNSResponses++ + s.Accepted++ +} + +// OnIgnored increases the counter of ignored and accepted connections. +func (s *Statistics) OnIgnored() { + s.Lock() + defer s.Unlock() + s.Ignored++ + s.Accepted++ +} + +func (s *Statistics) incMap(m *map[string]uint64, key string) { + if val, found := (*m)[key]; found == false { + // do we have enough space left? + nElems := len(*m) + if nElems >= s.maxStats { + // find the element with less hits + nMin := uint64(9999999999) + minKey := "" + for k, v := range *m { + if v < nMin { + minKey = k + nMin = v + } + } + // remove it + if minKey != "" { + delete(*m, minKey) + } + } + + (*m)[key] = 1 + } else { + (*m)[key] = val + 1 + } +} + +func (s *Statistics) eventWorker(id int) { + log.Debug("Stats worker #%d started.", id) + + for true { + select { + case job := <-s.jobs: + s.onConnection(job.con, job.match, job.wasMissed) + } + } +} + +func (s *Statistics) onConnection(con *conman.Connection, match *rule.Rule, wasMissed bool) { + s.Lock() + defer s.Unlock() + + s.Connections++ + + if wasMissed { + s.RuleMisses++ + } else { + s.RuleHits++ + } + + if wasMissed == false && match.Action == rule.Allow { + s.Accepted++ + } else { + s.Dropped++ + } + + s.incMap(&s.ByProto, con.Protocol) + s.incMap(&s.ByAddress, con.DstIP.String()) + if con.DstHost != "" { + s.incMap(&s.ByHost, con.DstHost) + } + s.incMap(&s.ByPort, strconv.FormatUint(uint64(con.DstPort), 10)) + s.incMap(&s.ByUID, strconv.Itoa(con.Entry.UserId)) + s.incMap(&s.ByExecutable, con.Process.Path) + + // if we reached the limit, shift everything back + // by one position + nEvents := len(s.Events) + if nEvents == s.maxEvents { + s.Events = s.Events[1:] + } + if wasMissed { + return + } + s.Events = append(s.Events, NewEvent(con, match)) +} + +func (s *Statistics) serializeEvents() []*protocol.Event { + nEvents := len(s.Events) + serialized := make([]*protocol.Event, nEvents) + + for i, e := range s.Events { + serialized[i] = e.Serialize() + } + + return serialized +} + +// emptyStats empties the stats once we've sent them to the GUI. +// We don't need them anymore here. +func (s *Statistics) emptyStats() { + s.Lock() + if len(s.Events) > 0 { + s.Events = make([]*Event, 0) + } + s.Unlock() +} + +// Serialize returns the collected statistics. +// After return the stats, the Events are emptied, to keep collecting more stats +// and not miss connections. +func (s *Statistics) Serialize() *protocol.Statistics { + s.Lock() + defer s.emptyStats() + defer s.Unlock() + + return &protocol.Statistics{ + DaemonVersion: core.Version, + Rules: uint64(s.rules.NumRules()), + Uptime: uint64(time.Since(s.Started).Seconds()), + DnsResponses: uint64(s.DNSResponses), + Connections: uint64(s.Connections), + Ignored: uint64(s.Ignored), + Accepted: uint64(s.Accepted), + Dropped: uint64(s.Dropped), + RuleHits: uint64(s.RuleHits), + RuleMisses: uint64(s.RuleMisses), + Events: s.serializeEvents(), + ByProto: s.ByProto, + ByAddress: s.ByAddress, + ByHost: s.ByHost, + ByPort: s.ByPort, + ByUid: s.ByUID, + ByExecutable: s.ByExecutable, + } +} diff --git a/daemon/system-fw.json b/daemon/system-fw.json new file mode 100644 index 0000000..1c1368b --- /dev/null +++ b/daemon/system-fw.json @@ -0,0 +1,255 @@ +{ + "Enabled": true, + "Version": 1, + "SystemRules": [ + { + "Rule": { + "Table": "mangle", + "Chain": "OUTPUT", + "Enabled": false, + "Position": "0", + "Description": "Allow icmp", + "Parameters": "-p icmp", + "Expressions": [], + "Target": "ACCEPT", + "TargetParameters": "" + }, + "Chains": [] + }, + { + "Chains": [ + { + "Name": "forward", + "Table": "filter", + "Family": "inet", + "Priority": "", + "Type": "filter", + "Hook": "forward", + "Policy": "accept", + "Rules": [] + }, + { + "Name": "output", + "Table": "filter", + "Family": "inet", + "Priority": "", + "Type": "filter", + "Hook": "output", + "Policy": "accept", + "Rules": [] + }, + { + "Name": "input", + "Table": "filter", + "Family": "inet", + "Priority": "", + "Type": "filter", + "Hook": "input", + "Policy": "accept", + "Rules": [ + { + "Enabled": false, + "Position": "0", + "Description": "Allow SSH server connections when input policy is DROP", + "Parameters": "", + "Expressions": [ + { + "Statement": { + "Op": "", + "Name": "tcp", + "Values": [ + { + "Key": "dport", + "Value": "22" + } + ] + } + } + ], + "Target": "accept", + "TargetParameters": "" + } + ] + }, + { + "Name": "filter-prerouting", + "Table": "nat", + "Family": "inet", + "Priority": "", + "Type": "filter", + "Hook": "prerouting", + "Policy": "accept", + "Rules": [] + }, + { + "Name": "prerouting", + "Table": "mangle", + "Family": "inet", + "Priority": "", + "Type": "mangle", + "Hook": "prerouting", + "Policy": "accept", + "Rules": [] + }, + { + "Name": "postrouting", + "Table": "mangle", + "Family": "inet", + "Priority": "", + "Type": "mangle", + "Hook": "postrouting", + "Policy": "accept", + "Rules": [] + }, + { + "Name": "prerouting", + "Table": "nat", + "Family": "inet", + "Priority": "", + "Type": "natdest", + "Hook": "prerouting", + "Policy": "accept", + "Rules": [] + }, + { + "Name": "postrouting", + "Table": "nat", + "Family": "inet", + "Priority": "", + "Type": "natsource", + "Hook": "postrouting", + "Policy": "accept", + "Rules": [] + }, + { + "Name": "input", + "Table": "nat", + "Family": "inet", + "Priority": "", + "Type": "natsource", + "Hook": "input", + "Policy": "accept", + "Rules": [] + }, + { + "Name": "output", + "Table": "nat", + "Family": "inet", + "Priority": "", + "Type": "natdest", + "Hook": "output", + "Policy": "accept", + "Rules": [] + }, + { + "Name": "output", + "Table": "mangle", + "Family": "inet", + "Priority": "", + "Type": "mangle", + "Hook": "output", + "Policy": "accept", + "Rules": [ + { + "Enabled": true, + "Position": "0", + "Description": "Allow ICMP", + "Expressions": [ + { + "Statement": { + "Op": "", + "Name": "icmp", + "Values": [ + { + "Key": "type", + "Value": "echo-request,echo-reply,destination-unreachable" + } + ] + } + } + ], + "Target": "accept", + "TargetParameters": "" + }, + { + "Enabled": true, + "Position": "0", + "Description": "Allow ICMPv6", + "Expressions": [ + { + "Statement": { + "Op": "", + "Name": "icmpv6", + "Values": [ + { + "Key": "type", + "Value": "echo-request,echo-reply,destination-unreachable" + } + ] + } + } + ], + "Target": "accept", + "TargetParameters": "" + }, + { + "Enabled": false, + "Position": "0", + "Description": "Exclude WireGuard VPN from being intercepted", + "Parameters": "", + "Expressions": [ + { + "Statement": { + "Op": "", + "Name": "udp", + "Values": [ + { + "Key": "dport", + "Value": "51820" + } + ] + } + } + ], + "Target": "accept", + "TargetParameters": "" + } + ] + }, + { + "Name": "forward", + "Table": "mangle", + "Family": "inet", + "Priority": "", + "Type": "mangle", + "Hook": "forward", + "Policy": "accept", + "Rules": [ + { + "UUID": "7d7394e1-100d-4b87-a90a-cd68c46edb0b", + "Enabled": false, + "Position": "0", + "Description": "Intercept forwarded connections (docker, etc)", + "Expressions": [ + { + "Statement": { + "Op": "", + "Name": "ct", + "Values": [ + { + "Key": "state", + "Value": "new" + } + ] + } + } + ], + "Target": "queue", + "TargetParameters": "num 0" + } + ] + } + ] + } + ] +} diff --git a/daemon/ui/alerts.go b/daemon/ui/alerts.go new file mode 100644 index 0000000..2bb8ae9 --- /dev/null +++ b/daemon/ui/alerts.go @@ -0,0 +1,125 @@ +package ui + +import ( + "time" + + "github.com/evilsocket/opensnitch/daemon/conman" + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/evilsocket/opensnitch/daemon/procmon" + "github.com/evilsocket/opensnitch/daemon/ui/protocol" + "golang.org/x/net/context" + "google.golang.org/grpc" + "google.golang.org/grpc/encoding/gzip" +) + +// NewWarningAlert builts a new warning alert +func NewWarningAlert(what protocol.Alert_What, data interface{}) *protocol.Alert { + return NewAlert(protocol.Alert_WARNING, what, protocol.Alert_SHOW_ALERT, protocol.Alert_MEDIUM, data) +} + +// NewErrorAlert builts a new error alert +func NewErrorAlert(what protocol.Alert_What, data interface{}) *protocol.Alert { + return NewAlert(protocol.Alert_ERROR, what, protocol.Alert_SHOW_ALERT, protocol.Alert_HIGH, data) +} + +// NewAlert builts a new generic alert +func NewAlert(atype protocol.Alert_Type, what protocol.Alert_What, action protocol.Alert_Action, prio protocol.Alert_Priority, data interface{}) *protocol.Alert { + a := &protocol.Alert{ + Id: uint64(time.Now().UnixNano()), + Type: atype, + Action: action, + What: what, + Priority: prio, + } + + switch what { + case protocol.Alert_KERNEL_EVENT: + + switch data.(type) { + case procmon.Process: + a.Data = &protocol.Alert_Proc{ + data.(*procmon.Process).Serialize(), + } + case string: + a.Data = &protocol.Alert_Text{data.(string)} + a.Action = protocol.Alert_SHOW_ALERT + } + case protocol.Alert_CONNECTION: + a.Data = &protocol.Alert_Conn{ + data.(*conman.Connection).Serialize(), + } + case protocol.Alert_GENERIC: + a.Data = &protocol.Alert_Text{data.(string)} + } + + return a +} + +// SendInfoAlert sends an info alert +func (c *Client) SendInfoAlert(data interface{}) { + c.PostAlert(protocol.Alert_INFO, protocol.Alert_GENERIC, protocol.Alert_SHOW_ALERT, protocol.Alert_LOW, data) +} + +// SendWarningAlert sends an warning alert +func (c *Client) SendWarningAlert(data interface{}) { + c.PostAlert(protocol.Alert_WARNING, protocol.Alert_GENERIC, protocol.Alert_SHOW_ALERT, protocol.Alert_MEDIUM, data) +} + +// SendErrorAlert sends an error alert +func (c *Client) SendErrorAlert(data interface{}) { + c.PostAlert(protocol.Alert_ERROR, protocol.Alert_GENERIC, protocol.Alert_SHOW_ALERT, protocol.Alert_HIGH, data) +} + +// alertsDispatcher waits to be connected to the GUI. +// Once connected, dispatches all the queued alerts. +func (c *Client) alertsDispatcher() { + queuedAlerts := make(chan protocol.Alert, 32) + connected := false + + isQueueFull := func(qdAlerts chan protocol.Alert) bool { return len(qdAlerts) > 31 } + isQueueEmpty := func(qdAlerts chan protocol.Alert) bool { return len(qdAlerts) == 0 } + queueAlert := func(qdAlerts chan protocol.Alert, pbAlert protocol.Alert) { + if isQueueFull(qdAlerts) { + v := <-qdAlerts + // empty queue before adding a new one + log.Debug("Discarding queued alert (%d): %v", len(qdAlerts), v) + } + select { + case qdAlerts <- pbAlert: + default: + log.Debug("Alert not sent to queue, full? (%d)", len(qdAlerts)) + } + } + + for { + select { + case pbAlert := <-c.alertsChan: + if !connected { + queueAlert(queuedAlerts, pbAlert) + continue + } + c.dispatchAlert(pbAlert) + case ready := <-c.isConnected: + connected = ready + if ready { + log.Important("UI connected, dispathing queued alerts: %d", len(c.alertsChan)) + for { + if isQueueEmpty(queuedAlerts) { + // no more queued alerts, exit + break + } + c.dispatchAlert(<-queuedAlerts) + } + } + } + } +} + +func (c *Client) dispatchAlert(pbAlert protocol.Alert) { + if c.client == nil { + return + } + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + c.client.PostAlert(ctx, &pbAlert, grpc.UseCompressor(gzip.Name)) + cancel() +} diff --git a/daemon/ui/auth/auth.go b/daemon/ui/auth/auth.go new file mode 100644 index 0000000..b02a7e8 --- /dev/null +++ b/daemon/ui/auth/auth.go @@ -0,0 +1,102 @@ +package auth + +import ( + "crypto/tls" + "crypto/x509" + "io/ioutil" + + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/evilsocket/opensnitch/daemon/ui/config" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" +) + +// client auth types: +// https://pkg.go.dev/crypto/tls#ClientAuthType +var ( + clientAuthType = map[string]tls.ClientAuthType{ + "no-client-cert": tls.NoClientCert, + "req-cert": tls.RequestClientCert, + "req-any-cert": tls.RequireAnyClientCert, + "verify-cert": tls.VerifyClientCertIfGiven, + "req-and-verify-cert": tls.RequireAndVerifyClientCert, + } +) + +const ( + // AuthSimple will use WithInsecure() + AuthSimple = "simple" + + // AuthTLSSimple will use a common CA certificate, shared between the server + // and all the clients. + AuthTLSSimple = "tls-simple" + + // AuthTLSMutual will use a CA certificate and a client cert and key + // to authenticate each client. + AuthTLSMutual = "tls-mutual" +) + +// New returns the configuration that the UI will use +// to connect with the server. +func New(config *config.Config) (grpc.DialOption, error) { + config.RLock() + + credsType := config.Server.Authentication.Type + tlsOpts := config.Server.Authentication.TLSOptions + + config.RUnlock() + + if credsType == "" || credsType == AuthSimple { + log.Debug("UI auth: simple") + return grpc.WithInsecure(), nil + } + certPool := x509.NewCertPool() + + // use CA certificate to authenticate clients if supplied + if tlsOpts.CACert != "" { + if caPem, err := ioutil.ReadFile(tlsOpts.CACert); err != nil { + log.Warning("reading UI auth CA certificate (%s): %s", credsType, err) + } else { + if !certPool.AppendCertsFromPEM(caPem) { + log.Warning("adding UI auth CA certificate (%s): %s", credsType, err) + } + } + } + + // use server certificate to authenticate clients if supplied + if tlsOpts.ServerCert != "" { + if serverPem, err := ioutil.ReadFile(tlsOpts.ServerCert); err != nil { + log.Warning("reading auth server cert: %s", err) + } else { + if !certPool.AppendCertsFromPEM(serverPem) { + log.Warning("adding UI auth server cert (%s): %s", credsType, err) + } + } + } + + // set config of tls credential + // https://pkg.go.dev/crypto/tls#Config + tlsCfg := &tls.Config{ + InsecureSkipVerify: tlsOpts.SkipVerify, + RootCAs: certPool, + } + + // https://pkg.go.dev/google.golang.org/grpc/credentials#SecurityLevel + if credsType == AuthTLSMutual { + tlsCfg.ClientAuth = clientAuthType[tlsOpts.ClientAuthType] + clientCert, err := tls.LoadX509KeyPair( + tlsOpts.ClientCert, + tlsOpts.ClientKey, + ) + if err != nil { + return nil, err + } + log.Debug(" using client cert: %s", tlsOpts.ClientCert) + log.Debug(" using client key: %s", tlsOpts.ClientKey) + tlsCfg.Certificates = []tls.Certificate{clientCert} + } + + return grpc.WithTransportCredentials( + credentials.NewTLS(tlsCfg), + ), nil +} diff --git a/daemon/ui/client.go b/daemon/ui/client.go new file mode 100644 index 0000000..be013d2 --- /dev/null +++ b/daemon/ui/client.go @@ -0,0 +1,368 @@ +package ui + +import ( + "fmt" + "net" + "sync" + "time" + + "github.com/evilsocket/opensnitch/daemon/conman" + "github.com/evilsocket/opensnitch/daemon/firewall/iptables" + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/evilsocket/opensnitch/daemon/log/loggers" + "github.com/evilsocket/opensnitch/daemon/rule" + "github.com/evilsocket/opensnitch/daemon/statistics" + "github.com/evilsocket/opensnitch/daemon/ui/auth" + "github.com/evilsocket/opensnitch/daemon/ui/config" + "github.com/evilsocket/opensnitch/daemon/ui/protocol" + + "github.com/fsnotify/fsnotify" + "golang.org/x/net/context" + "google.golang.org/grpc" + "google.golang.org/grpc/connectivity" + "google.golang.org/grpc/keepalive" +) + +var ( + configFile = "/etc/opensnitchd/default-config.json" + dummyOperator, _ = rule.NewOperator(rule.Simple, false, rule.OpTrue, "", make([]rule.Operator, 0)) + clientDisconnectedRule = rule.Create("ui.client.disconnected", "", true, false, false, rule.Allow, rule.Once, dummyOperator) + // While the GUI is connected, deny by default everything until the user takes an action. + clientConnectedRule = rule.Create("ui.client.connected", "", true, false, false, rule.Deny, rule.Once, dummyOperator) + clientErrorRule = rule.Create("ui.client.error", "", true, false, false, rule.Allow, rule.Once, dummyOperator) + clientConfig config.Config + + maxQueuedAlerts = 1024 +) + +// Client holds the connection information of a client. +type Client struct { + rules *rule.Loader + stats *statistics.Statistics + con *grpc.ClientConn + configWatcher *fsnotify.Watcher + client protocol.UIClient + clientCtx context.Context + clientCancel context.CancelFunc + streamNotifications protocol.UI_NotificationsClient + isConnected chan bool + alertsChan chan protocol.Alert + socketPath string + unixSockPrefix string + //isAsking is set to true if the client is awaiting a decision from the GUI + isAsking bool + isUnixSocket bool + + sync.RWMutex +} + +// NewClient creates and configures a new client. +func NewClient(socketPath, localConfigFile string, stats *statistics.Statistics, rules *rule.Loader, loggers *loggers.LoggerManager) *Client { + if localConfigFile != "" { + configFile = localConfigFile + } + c := &Client{ + stats: stats, + rules: rules, + isUnixSocket: false, + isAsking: false, + isConnected: make(chan bool), + alertsChan: make(chan protocol.Alert, maxQueuedAlerts), + } + //for i := 0; i < 4; i++ { + go c.alertsDispatcher() + + c.clientCtx, c.clientCancel = context.WithCancel(context.Background()) + + if watcher, err := fsnotify.NewWatcher(); err == nil { + c.configWatcher = watcher + } + c.loadDiskConfiguration(false) + if socketPath != "" { + c.setSocketPath(c.getSocketPath(socketPath)) + } + loggers.Load(clientConfig.Server.Loggers, clientConfig.Stats.Workers) + stats.SetLimits(clientConfig.Stats) + stats.SetLoggers(loggers) + + return c +} + +// Connect starts the connection poller +func (c *Client) Connect() { + go c.poller() +} + +// Close cancels the running tasks: pinging the server and (re)connection poller. +func (c *Client) Close() { + c.clientCancel() +} + +// ProcMonitorMethod returns the monitor method configured. +// If it's not present in the config file, it'll return an empty string. +func (c *Client) ProcMonitorMethod() string { + clientConfig.RLock() + defer clientConfig.RUnlock() + return clientConfig.ProcMonitorMethod +} + +// InterceptUnknown returns +func (c *Client) InterceptUnknown() bool { + clientConfig.RLock() + defer clientConfig.RUnlock() + return clientConfig.InterceptUnknown +} + +// GetFirewallType returns the firewall to use +func (c *Client) GetFirewallType() string { + clientConfig.RLock() + defer clientConfig.RUnlock() + if clientConfig.Firewall == "" { + return iptables.Name + } + return clientConfig.Firewall +} + +// DefaultAction returns the default configured action for +func (c *Client) DefaultAction() rule.Action { + isConnected := c.Connected() + + c.RLock() + defer c.RUnlock() + + if isConnected { + return clientConnectedRule.Action + } + + return clientDisconnectedRule.Action +} + +// DefaultDuration returns the default duration configured for a rule. +// For example it can be: once, always, "until restart". +func (c *Client) DefaultDuration() rule.Duration { + c.RLock() + defer c.RUnlock() + return clientDisconnectedRule.Duration +} + +// Connected checks if the client has established a connection with the server. +func (c *Client) Connected() bool { + c.RLock() + defer c.RUnlock() + if c.con == nil || c.con.GetState() != connectivity.Ready { + return false + } + return true +} + +// GetIsAsking returns the isAsking flag +func (c *Client) GetIsAsking() bool { + c.RLock() + defer c.RUnlock() + return c.isAsking +} + +// SetIsAsking sets the isAsking flag +func (c *Client) SetIsAsking(flag bool) { + c.Lock() + defer c.Unlock() + c.isAsking = flag +} + +func (c *Client) poller() { + log.Debug("UI service poller started for socket %s", c.socketPath) + wasConnected := false + for { + select { + case <-c.clientCtx.Done(): + log.Info("Client.poller() exit, Done()") + goto Exit + default: + isConnected := c.Connected() + if wasConnected != isConnected { + c.onStatusChange(isConnected) + wasConnected = isConnected + } + + if c.Connected() == false { + // connect and create the client if needed + if err := c.connect(); err != nil { + log.Warning("Error while connecting to UI service: %s", err) + } + } + if c.Connected() == true { + // if the client is connected and ready, send a ping + if err := c.ping(time.Now()); err != nil { + log.Warning("Error while pinging UI service: %s, state: %v", err, c.con.GetState()) + } + } + + time.Sleep(1 * time.Second) + } + } +Exit: + log.Info("uiClient exit") +} + +func (c *Client) onStatusChange(connected bool) { + if connected { + log.Info("Connected to the UI service on %s", c.socketPath) + go c.Subscribe() + + select { + case c.isConnected <- true: + default: + } + } else { + log.Error("Connection to the UI service lost.") + c.disconnect() + } +} + +func (c *Client) connect() (err error) { + if c.Connected() { + return + } + + if c.con != nil { + if c.con.GetState() == connectivity.TransientFailure || c.con.GetState() == connectivity.Shutdown { + c.disconnect() + } else { + return + } + } + + if err := c.openSocket(); err != nil { + log.Debug("connect() %s", err) + c.disconnect() + return err + } + + if c.client == nil { + c.client = protocol.NewUIClient(c.con) + } + return nil +} + +func (c *Client) openSocket() (err error) { + c.Lock() + defer c.Unlock() + + dialOption, err := auth.New(&clientConfig) + if err != nil { + return fmt.Errorf("Invalid client auth options: %s", err) + } + if c.isUnixSocket { + c.con, err = grpc.Dial(c.socketPath, dialOption, + grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) { + return net.DialTimeout(c.unixSockPrefix, addr, timeout) + })) + } else { + // https://pkg.go.dev/google.golang.org/grpc/keepalive#ClientParameters + var kacp = keepalive.ClientParameters{ + Time: 5 * time.Second, + // if there's no activity after ^, wait 20s and close + // server timeout is 20s by default. + Timeout: 22 * time.Second, + // send pings even without active streams + PermitWithoutStream: true, + } + + c.con, err = grpc.Dial(c.socketPath, dialOption, grpc.WithKeepaliveParams(kacp)) + } + + return err +} + +func (c *Client) disconnect() { + c.Lock() + defer c.Unlock() + + select { + case c.isConnected <- false: + default: + } + if c.con != nil { + c.con.Close() + c.con = nil + log.Debug("client.disconnect()") + } + c.client = nil +} + +func (c *Client) ping(ts time.Time) (err error) { + if c.Connected() == false { + return fmt.Errorf("service is not connected") + } + + c.Lock() + defer c.Unlock() + + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + reqID := uint64(ts.UnixNano()) + + pReq := &protocol.PingRequest{ + Id: reqID, + Stats: c.stats.Serialize(), + } + c.stats.RLock() + pong, err := c.client.Ping(ctx, pReq) + c.stats.RUnlock() + if err != nil { + return err + } + + if pong.Id != reqID { + return fmt.Errorf("Expected pong with id 0x%x, got 0x%x", reqID, pong.Id) + } + + return nil +} + +// Ask sends a request to the server, with the values of a connection to be +// allowed or denied. +func (c *Client) Ask(con *conman.Connection) *rule.Rule { + if c.client == nil { + return nil + } + + // FIXME: if timeout is fired, the rule is not added to the list in the GUI + ctx, cancel := context.WithTimeout(context.Background(), time.Second*120) + defer cancel() + reply, err := c.client.AskRule(ctx, con.Serialize()) + if err != nil { + log.Warning("Error while asking for rule: %s - %v", err, con) + return nil + } + + r, err := rule.Deserialize(reply) + if err != nil { + return nil + } + return r +} + +// PostAlert queues a new message to be delivered to the server +func (c *Client) PostAlert(atype protocol.Alert_Type, awhat protocol.Alert_What, action protocol.Alert_Action, prio protocol.Alert_Priority, data interface{}) { + if len(c.alertsChan) > maxQueuedAlerts-1 { + // pop oldest alert if channel is full + log.Debug("PostAlert() queue full, popping alert (%d)", len(c.alertsChan)) + <-c.alertsChan + } + if c.Connected() == false { + log.Debug("UI not connected, queueing alert: %d", len(c.alertsChan)) + } + c.alertsChan <- *NewAlert(atype, awhat, action, prio, data) +} + +func (c *Client) monitorConfigWorker() { + for { + select { + case event := <-c.configWatcher.Events: + if (event.Op&fsnotify.Write == fsnotify.Write) || (event.Op&fsnotify.Remove == fsnotify.Remove) { + c.loadDiskConfiguration(true) + } + } + } +} diff --git a/daemon/ui/client_test.go b/daemon/ui/client_test.go new file mode 100644 index 0000000..c3de0a6 --- /dev/null +++ b/daemon/ui/client_test.go @@ -0,0 +1,91 @@ +package ui + +import ( + "encoding/json" + "testing" + "time" + + "github.com/evilsocket/opensnitch/daemon/core" + "github.com/evilsocket/opensnitch/daemon/firewall/iptables" + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/evilsocket/opensnitch/daemon/log/loggers" + "github.com/evilsocket/opensnitch/daemon/procmon" + "github.com/evilsocket/opensnitch/daemon/rule" + "github.com/evilsocket/opensnitch/daemon/statistics" + "github.com/evilsocket/opensnitch/daemon/ui/config" +) + +var ( + defaultConfig = &config.Config{ + ProcMonitorMethod: procmon.MethodEbpf, + DefaultAction: "allow", + DefaultDuration: "once", + InterceptUnknown: false, + Firewall: "nftables", + } + reloadConfig = *defaultConfig +) + +func restoreConfigFile(t *testing.T) { + // start from a clean state + if _, err := core.Exec("cp", []string{ + // unmodified default config + "./testdata/orig-default-config.json", + // config will be modified by some tests + "./testdata/default-config.json", + }); err != nil { + t.Errorf("error copying default config file: %s", err) + } +} + +func validateConfig(t *testing.T, uiClient *Client, cfg *config.Config) { + if uiClient.ProcMonitorMethod() != cfg.ProcMonitorMethod { + t.Errorf("not expected ProcMonitorMethod value: %s, expected: %s", uiClient.ProcMonitorMethod(), cfg.ProcMonitorMethod) + } + if uiClient.GetFirewallType() != cfg.Firewall { + t.Errorf("not expected FirewallType value: %s, expected: %s", uiClient.GetFirewallType(), cfg.Firewall) + } + if uiClient.InterceptUnknown() != cfg.InterceptUnknown { + t.Errorf("not expected InterceptUnknown value: %v, expected: %v", uiClient.InterceptUnknown(), cfg.InterceptUnknown) + } + if uiClient.DefaultAction() != rule.Action(cfg.DefaultAction) { + t.Errorf("not expected DefaultAction value: %s, expected: %s", clientDisconnectedRule.Action, cfg.DefaultAction) + } +} + +func TestClientConfig(t *testing.T) { + restoreConfigFile(t) + cfgFile := "./testdata/default-config.json" + + rules, err := rule.NewLoader(false) + if err != nil { + log.Fatal("") + } + + stats := statistics.New(rules) + loggerMgr := loggers.NewLoggerManager() + uiClient := NewClient("unix:///tmp/osui.sock", cfgFile, stats, rules, loggerMgr) + + t.Run("validate-load-config", func(t *testing.T) { + validateConfig(t, uiClient, defaultConfig) + }) + + t.Run("validate-reload-config", func(t *testing.T) { + reloadConfig.ProcMonitorMethod = procmon.MethodProc + reloadConfig.DefaultAction = string(rule.Deny) + reloadConfig.InterceptUnknown = true + reloadConfig.Firewall = iptables.Name + reloadConfig.Server.Address = "unix:///run/user/1000/opensnitch/osui.sock" + + plainJSON, err := json.Marshal(reloadConfig) + if err != nil { + t.Errorf("Error marshalling config: %s", err) + } + if err = config.Save(configFile, string(plainJSON)); err != nil { + t.Errorf("error saving config to disk: %s", err) + } + time.Sleep(time.Second * 3) + + validateConfig(t, uiClient, &reloadConfig) + }) +} diff --git a/daemon/ui/config/config.go b/daemon/ui/config/config.go new file mode 100644 index 0000000..afeef8a --- /dev/null +++ b/daemon/ui/config/config.go @@ -0,0 +1,117 @@ +package config + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "reflect" + "sync" + + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/evilsocket/opensnitch/daemon/log/loggers" + "github.com/evilsocket/opensnitch/daemon/statistics" +) + +type ( + serverTLSOptions struct { + CACert string `json:"CACert"` + ServerCert string `json:"ServerCert"` + ServerKey string `json:"ServerKey"` + ClientCert string `json:"ClientCert"` + ClientKey string `json:"ClientKey"` + // https://pkg.go.dev/crypto/tls#ClientAuthType + ClientAuthType string `json:"ClientAuthType"` + // https://pkg.go.dev/crypto/tls#Config + SkipVerify bool `json:"SkipVerify"` + + // https://pkg.go.dev/crypto/tls#Conn.VerifyHostname + // VerifyHostname bool + // https://pkg.go.dev/crypto/tls#example-Config-VerifyConnection + // VerifyConnection bool + // VerifyPeerCertificate bool + } + + serverAuth struct { + // token?, google?, simple-tls, mutual-tls + Type string `json:"Type"` + TLSOptions serverTLSOptions `json:"TLSOptions"` + } + + serverConfig struct { + Loggers []loggers.LoggerConfig `json:"Loggers"` + Address string `json:"Address"` + LogFile string `json:"LogFile"` + Authentication serverAuth `json:"Authentication"` + } + + rulesOptions struct { + Path string `json:"Path"` + } + + ebpfOptions struct { + ModulesPath string `json:"ModulesPath"` + } + + // InternalOptions struct + internalOptions struct { + GCPercent int `json:"GCPercent"` + FlushConnsOnStart bool `json:"FlushConnsOnStart"` + } +) + +// Config holds the values loaded from configFile +type Config struct { + LogLevel *int32 `json:"LogLevel"` + Firewall string `json:"Firewall"` + DefaultAction string `json:"DefaultAction"` + DefaultDuration string `json:"DefaultDuration"` + ProcMonitorMethod string `json:"ProcMonitorMethod"` + Ebpf ebpfOptions `json:"Ebpf"` + Rules rulesOptions `json:"Rules"` + Server serverConfig `json:"Server"` + Stats statistics.StatsConfig `json:"Stats"` + Internal internalOptions `json:"Internal"` + + InterceptUnknown bool `json:"InterceptUnknown"` + LogUTC bool `json:"LogUTC"` + LogMicro bool `json:"LogMicro"` + + sync.RWMutex +} + +// Parse determines if the given configuration is ok. +func Parse(rawConfig interface{}) (conf Config, err error) { + if vt := reflect.ValueOf(rawConfig).Kind(); vt == reflect.String { + err = json.Unmarshal([]byte((rawConfig.(string))), &conf) + } else { + err = json.Unmarshal(rawConfig.([]uint8), &conf) + } + return conf, err +} + +// Load loads the content of a file from disk. +func Load(configFile string) ([]byte, error) { + raw, err := ioutil.ReadFile(configFile) + if err != nil || len(raw) == 0 { + return nil, err + } + + return raw, nil +} + +// Save writes daemon configuration to disk. +func Save(configFile, rawConfig string) (err error) { + if _, err = Parse(rawConfig); err != nil { + return fmt.Errorf("Error parsing configuration %s: %s", rawConfig, err) + } + + if err = os.Chmod(configFile, 0600); err != nil { + log.Warning("unable to set permissions to default config: %s", err) + } + if err = ioutil.WriteFile(configFile, []byte(rawConfig), 0644); err != nil { + log.Error("writing configuration to disk: %s", err) + return err + } + return nil +} diff --git a/daemon/ui/config_utils.go b/daemon/ui/config_utils.go new file mode 100644 index 0000000..a6a1a96 --- /dev/null +++ b/daemon/ui/config_utils.go @@ -0,0 +1,145 @@ +package ui + +import ( + "fmt" + "strings" + + "runtime/debug" + + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/evilsocket/opensnitch/daemon/netlink" + "github.com/evilsocket/opensnitch/daemon/procmon/monitor" + "github.com/evilsocket/opensnitch/daemon/rule" + "github.com/evilsocket/opensnitch/daemon/ui/config" +) + +func (c *Client) getSocketPath(socketPath string) string { + c.Lock() + defer c.Unlock() + + if strings.HasPrefix(socketPath, "unix:") == true { + c.isUnixSocket = true + c.unixSockPrefix = "unix" + return socketPath[5:] + } + if strings.HasPrefix(socketPath, "unix-abstract:") == true { + c.isUnixSocket = true + c.unixSockPrefix = "unix-abstract" + return socketPath[14:] + } + + c.isUnixSocket = false + return socketPath +} + +func (c *Client) setSocketPath(socketPath string) { + c.Lock() + defer c.Unlock() + + c.socketPath = socketPath +} + +func (c *Client) isProcMonitorEqual(newMonitorMethod string) bool { + clientConfig.RLock() + defer clientConfig.RUnlock() + + return newMonitorMethod == clientConfig.ProcMonitorMethod +} + +func (c *Client) loadDiskConfiguration(reload bool) { + raw, err := config.Load(configFile) + if err != nil || len(raw) == 0 { + // Sometimes we may receive 2 Write events on monitorConfigWorker, + // Which may lead to read 0 bytes. + log.Warning("Error loading configuration from disk %s: %s", configFile, err) + return + } + + if ok := c.loadConfiguration(raw); ok { + if err := c.configWatcher.Add(configFile); err != nil { + log.Error("Could not watch path: %s", err) + return + } + } + + if reload { + return + } + + go c.monitorConfigWorker() +} + +func (c *Client) loadConfiguration(rawConfig []byte) bool { + var err error + clientConfig, err = config.Parse(rawConfig) + if err != nil { + msg := fmt.Sprintf("Error parsing configuration %s: %s", configFile, err) + log.Error(msg) + c.SendWarningAlert(msg) + return false + } + + clientConfig.Lock() + defer clientConfig.Unlock() + + // firstly load config level, to detect further errors if any + if clientConfig.LogLevel != nil { + log.SetLogLevel(int(*clientConfig.LogLevel)) + } + log.SetLogUTC(clientConfig.LogUTC) + log.SetLogMicro(clientConfig.LogMicro) + if clientConfig.Server.LogFile != "" { + log.Close() + log.OpenFile(clientConfig.Server.LogFile) + } + + if clientConfig.Server.Address != "" { + tempSocketPath := c.getSocketPath(clientConfig.Server.Address) + if tempSocketPath != c.socketPath { + // disconnect, and let the connection poller reconnect to the new address + c.disconnect() + } + c.setSocketPath(tempSocketPath) + } + if clientConfig.DefaultAction != "" { + clientDisconnectedRule.Action = rule.Action(clientConfig.DefaultAction) + clientErrorRule.Action = rule.Action(clientConfig.DefaultAction) + // TODO: reconfigure connected rule if changed, but not save it to disk. + //clientConnectedRule.Action = rule.Action(clientConfig.DefaultAction) + } + if clientConfig.DefaultDuration != "" { + clientDisconnectedRule.Duration = rule.Duration(clientConfig.DefaultDuration) + clientErrorRule.Duration = rule.Duration(clientConfig.DefaultDuration) + } + + reloaded := false + if clientConfig.ProcMonitorMethod != "" { + err := monitor.ReconfigureMonitorMethod(clientConfig.ProcMonitorMethod, clientConfig.Ebpf.ModulesPath) + if err != nil { + msg := fmt.Sprintf("Unable to set new process monitor (%s) method from disk: %v", clientConfig.ProcMonitorMethod, err.Msg) + log.Warning(msg) + c.SendWarningAlert(msg) + } else { + reloaded = true + } + } + + if reloaded && clientConfig.Internal.FlushConnsOnStart { + log.Debug("[config] flushing established connections") + netlink.FlushConnections() + } else { + log.Debug("[config] not flushing established connections") + } + + if clientConfig.Internal.GCPercent > 0 { + oldgcpercent := debug.SetGCPercent(clientConfig.Internal.GCPercent) + log.Info("GC percent set to %d, previously was %d", clientConfig.Internal.GCPercent, oldgcpercent) + } + + // TODO: + //c.stats.SetLimits(clientConfig.Stats) + //loggers.Load(clientConfig.Server.Loggers, clientConfig.Stats.Workers) + //stats.SetLoggers(loggers) + + return true +} diff --git a/daemon/ui/notifications.go b/daemon/ui/notifications.go new file mode 100644 index 0000000..9acb665 --- /dev/null +++ b/daemon/ui/notifications.go @@ -0,0 +1,370 @@ +package ui + +import ( + "encoding/json" + "fmt" + "io" + "io/ioutil" + "strconv" + "strings" + "time" + + "github.com/evilsocket/opensnitch/daemon/core" + "github.com/evilsocket/opensnitch/daemon/firewall" + "github.com/evilsocket/opensnitch/daemon/log" + "github.com/evilsocket/opensnitch/daemon/procmon" + "github.com/evilsocket/opensnitch/daemon/procmon/monitor" + "github.com/evilsocket/opensnitch/daemon/rule" + "github.com/evilsocket/opensnitch/daemon/ui/config" + "github.com/evilsocket/opensnitch/daemon/ui/protocol" + "golang.org/x/net/context" +) + +var stopMonitoringProcess = make(chan int) + +// NewReply constructs a new protocol notification reply +func NewReply(rID uint64, replyCode protocol.NotificationReplyCode, data string) *protocol.NotificationReply { + return &protocol.NotificationReply{ + Id: rID, + Code: replyCode, + Data: data, + } +} + +func (c *Client) getClientConfig() *protocol.ClientConfig { + raw, _ := ioutil.ReadFile(configFile) + nodeName := core.GetHostname() + nodeVersion := core.GetKernelVersion() + var ts time.Time + rulesTotal := len(c.rules.GetAll()) + ruleList := make([]*protocol.Rule, rulesTotal) + idx := 0 + for _, r := range c.rules.GetAll() { + ruleList[idx] = r.Serialize() + idx++ + } + sysfw, err := firewall.Serialize() + if err != nil { + log.Warning("firewall.Serialize() error: %s", err) + } + return &protocol.ClientConfig{ + Id: uint64(ts.UnixNano()), + Name: nodeName, + Version: nodeVersion, + IsFirewallRunning: firewall.IsRunning(), + Config: strings.Replace(string(raw), "\n", "", -1), + LogLevel: uint32(log.MinLevel), + Rules: ruleList, + SystemFirewall: sysfw, + } +} + +func (c *Client) monitorProcessDetails(pid int, stream protocol.UI_NotificationsClient, notification *protocol.Notification) { + p := procmon.NewProcess(pid, "") + p.GetInfo() + ticker := time.NewTicker(2 * time.Second) + + for { + select { + case _pid := <-stopMonitoringProcess: + if _pid != pid { + continue + } + goto Exit + case <-ticker.C: + if err := p.GetExtraInfo(); err != nil { + c.sendNotificationReply(stream, notification.Id, notification.Data, err) + goto Exit + } + + pJSON, err := json.Marshal(p) + notification.Data = string(pJSON) + if errs := c.sendNotificationReply(stream, notification.Id, notification.Data, err); errs != nil { + goto Exit + } + } + } + +Exit: + ticker.Stop() +} + +func (c *Client) handleActionChangeConfig(stream protocol.UI_NotificationsClient, notification *protocol.Notification) { + log.Info("[notification] Reloading configuration") + // Parse received configuration first, to get the new proc monitor method. + newConf, err := config.Parse(notification.Data) + if err != nil { + log.Warning("[notification] error parsing received config: %v", notification.Data) + c.sendNotificationReply(stream, notification.Id, "", err) + return + } + + if c.GetFirewallType() != newConf.Firewall { + firewall.ChangeFw(newConf.Firewall) + } + + if err := monitor.ReconfigureMonitorMethod( + newConf.ProcMonitorMethod, + clientConfig.Ebpf.ModulesPath, + ); err != nil { + c.sendNotificationReply(stream, notification.Id, "", err.Msg) + return + } + + // this save operation triggers a re-loadConfiguration() + err = config.Save(configFile, notification.Data) + if err != nil { + log.Warning("[notification] CHANGE_CONFIG not applied %s", err) + } + + c.sendNotificationReply(stream, notification.Id, "", err) +} + +func (c *Client) handleActionEnableRule(stream protocol.UI_NotificationsClient, notification *protocol.Notification) { + var err error + for _, rul := range notification.Rules { + log.Info("[notification] enable rule: %s", rul.Name) + // protocol.Rule(protobuf) != rule.Rule(json) + r, _ := rule.Deserialize(rul) + r.Enabled = true + // save to disk only if the duration is rule.Always + err = c.rules.Replace(r, r.Duration == rule.Always) + } + c.sendNotificationReply(stream, notification.Id, "", err) +} + +func (c *Client) handleActionDisableRule(stream protocol.UI_NotificationsClient, notification *protocol.Notification) { + var err error + for _, rul := range notification.Rules { + log.Info("[notification] disable rule: %s", rul) + r, _ := rule.Deserialize(rul) + r.Enabled = false + err = c.rules.Replace(r, r.Duration == rule.Always) + } + c.sendNotificationReply(stream, notification.Id, "", err) +} + +func (c *Client) handleActionChangeRule(stream protocol.UI_NotificationsClient, notification *protocol.Notification) { + var rErr error + for _, rul := range notification.Rules { + r, err := rule.Deserialize(rul) + if r == nil { + rErr = fmt.Errorf("Invalid rule, %s", err) + continue + } + log.Info("[notification] change rule: %s %d", r, notification.Id) + if err := c.rules.Replace(r, r.Duration == rule.Always); err != nil { + log.Warning("[notification] Error changing rule: %s %s", err, r) + rErr = err + } + } + c.sendNotificationReply(stream, notification.Id, "", rErr) +} + +func (c *Client) handleActionDeleteRule(stream protocol.UI_NotificationsClient, notification *protocol.Notification) { + var err error + for _, rul := range notification.Rules { + log.Info("[notification] delete rule: %s %d", rul.Name, notification.Id) + err = c.rules.Delete(rul.Name) + if err != nil { + log.Error("[notification] Error deleting rule: %s %s", err, rul) + } + } + c.sendNotificationReply(stream, notification.Id, "", err) +} + +func (c *Client) handleActionMonitorProcess(stream protocol.UI_NotificationsClient, notification *protocol.Notification) { + pid, err := strconv.Atoi(notification.Data) + if err != nil { + log.Error("parsing PID to monitor: %d, err: %s", pid, err) + return + } + if !core.Exists(fmt.Sprint("/proc/", pid)) { + c.sendNotificationReply(stream, notification.Id, "", fmt.Errorf("The process is no longer running")) + return + } + go c.monitorProcessDetails(pid, stream, notification) +} + +func (c *Client) handleActionStopMonitorProcess(stream protocol.UI_NotificationsClient, notification *protocol.Notification) { + pid, err := strconv.Atoi(notification.Data) + if err != nil { + log.Error("parsing PID to stop monitor: %d, err: %s", pid, err) + c.sendNotificationReply(stream, notification.Id, "", fmt.Errorf("Error stopping monitor: %s", notification.Data)) + return + } + stopMonitoringProcess <- pid + c.sendNotificationReply(stream, notification.Id, "", nil) +} + +func (c *Client) handleActionReloadFw(stream protocol.UI_NotificationsClient, notification *protocol.Notification) { + log.Info("[notification] reloading firewall") + + sysfw, err := firewall.Deserialize(notification.SysFirewall) + if err != nil { + log.Warning("firewall.Deserialize() error: %s", err) + c.sendNotificationReply(stream, notification.Id, "", fmt.Errorf("Error reloading firewall, invalid rules")) + return + } + if err := firewall.SaveConfiguration(sysfw); err != nil { + c.sendNotificationReply(stream, notification.Id, "", fmt.Errorf("Error saving system firewall rules: %s", err)) + return + } + // TODO: + // - add new API endpoints to delete, add or change rules atomically. + // - a global goroutine where errors can be sent to the server (GUI). + go func(c *Client) { + var errors string + for { + select { + case fwerr := <-firewall.ErrorsChan(): + errors = fmt.Sprint(errors, fwerr, ",") + if firewall.ErrChanEmpty() { + goto ExitWithError + } + + // FIXME: can this operation last longer than 2s? if there're more than.. 100...10000 rules? + case <-time.After(2 * time.Second): + log.Debug("[notification] reload firewall. timeout fired, no errors?") + c.sendNotificationReply(stream, notification.Id, "", nil) + goto Exit + + } + } + ExitWithError: + c.sendNotificationReply(stream, notification.Id, "", fmt.Errorf("%s", errors)) + Exit: + }(c) + +} + +func (c *Client) handleNotification(stream protocol.UI_NotificationsClient, notification *protocol.Notification) { + switch { + case notification.Type == protocol.Action_MONITOR_PROCESS: + c.handleActionMonitorProcess(stream, notification) + + case notification.Type == protocol.Action_STOP_MONITOR_PROCESS: + c.handleActionStopMonitorProcess(stream, notification) + + case notification.Type == protocol.Action_CHANGE_CONFIG: + c.handleActionChangeConfig(stream, notification) + + case notification.Type == protocol.Action_ENABLE_INTERCEPTION: + log.Info("[notification] starting interception") + if err := firewall.EnableInterception(); err != nil { + log.Warning("firewall.EnableInterception() error: %s", err) + c.sendNotificationReply(stream, notification.Id, "", err) + return + } + c.sendNotificationReply(stream, notification.Id, "", nil) + + case notification.Type == protocol.Action_DISABLE_INTERCEPTION: + log.Info("[notification] stopping interception") + if err := firewall.DisableInterception(); err != nil { + log.Warning("firewall.DisableInterception() error: %s", err) + c.sendNotificationReply(stream, notification.Id, "", err) + return + } + c.sendNotificationReply(stream, notification.Id, "", nil) + + case notification.Type == protocol.Action_RELOAD_FW_RULES: + c.handleActionReloadFw(stream, notification) + + // ENABLE_RULE just replaces the rule on disk + case notification.Type == protocol.Action_ENABLE_RULE: + c.handleActionEnableRule(stream, notification) + + case notification.Type == protocol.Action_DISABLE_RULE: + c.handleActionDisableRule(stream, notification) + + case notification.Type == protocol.Action_DELETE_RULE: + c.handleActionDeleteRule(stream, notification) + + // CHANGE_RULE can add() or replace) an existing rule. + case notification.Type == protocol.Action_CHANGE_RULE: + c.handleActionChangeRule(stream, notification) + } +} + +func (c *Client) sendNotificationReply(stream protocol.UI_NotificationsClient, nID uint64, data string, err error) error { + reply := NewReply(nID, protocol.NotificationReplyCode_OK, data) + if err != nil { + reply.Code = protocol.NotificationReplyCode_ERROR + reply.Data = fmt.Sprint(err) + } + if err := stream.Send(reply); err != nil { + log.Error("Error replying to notification: %s %d", err, reply.Id) + return err + } + + return nil +} + +// Subscribe opens a connection with the server (UI), to start +// receiving notifications. +// It firstly sends the daemon status and configuration. +func (c *Client) Subscribe() { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) + defer cancel() + + clientCfg, err := c.client.Subscribe(ctx, c.getClientConfig()) + if err != nil { + log.Error("Subscribing to GUI %s", err) + // When connecting to the GUI via TCP, sometimes the notifications channel is + // not established, and the main channel is never closed. + // We need to disconnect everything after a timeout and try it again. + c.disconnect() + return + } + + if tempConf, err := config.Parse(clientCfg.Config); err == nil { + c.Lock() + clientConnectedRule.Action = rule.Action(tempConf.DefaultAction) + c.Unlock() + } + c.listenForNotifications() +} + +// Notifications is the channel where the daemon receives messages from the server. +// It consists of 2 grpc streams (send/receive) that are never closed, +// this way we can share messages in realtime. +// If the GUI is closed, we'll receive an error reading from the channel. +func (c *Client) listenForNotifications() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // open the stream channel + streamReply := &protocol.NotificationReply{Id: 0, Code: protocol.NotificationReplyCode_OK} + notisStream, err := c.client.Notifications(ctx) + if err != nil { + log.Error("establishing notifications channel %s", err) + return + } + // send the first notification + if err := notisStream.Send(streamReply); err != nil { + log.Error("sending notification HELLO %s", err) + return + } + log.Info("Start receiving notifications") + for { + select { + case <-c.clientCtx.Done(): + goto Exit + default: + noti, err := notisStream.Recv() + if err == io.EOF { + log.Warning("notification channel closed by the server") + goto Exit + } + if err != nil { + log.Error("getting notifications: %s %s", err, noti) + goto Exit + } + c.handleNotification(notisStream, noti) + } + } +Exit: + notisStream.CloseSend() + log.Info("Stop receiving notifications") + c.disconnect() +} diff --git a/daemon/ui/protocol/.gitkeep b/daemon/ui/protocol/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/daemon/ui/testdata/default-config.json b/daemon/ui/testdata/default-config.json new file mode 100644 index 0000000..fca39f7 --- /dev/null +++ b/daemon/ui/testdata/default-config.json @@ -0,0 +1 @@ +{"Server":{"Address":"unix:///run/user/1000/opensnitch/osui.sock","Authentication":{"Type":"","TLSOptions":{"CACert":"","ServerCert":"","ServerKey":"","ClientCert":"","ClientKey":"","SkipVerify":false,"ClientAuthType":""}},"LogFile":"","Loggers":null},"DefaultAction":"deny","DefaultDuration":"once","InterceptUnknown":true,"ProcMonitorMethod":"proc","LogLevel":null,"LogUTC":false,"LogMicro":false,"Firewall":"iptables","Stats":{"MaxEvents":0,"MaxStats":0,"Workers":0}} \ No newline at end of file diff --git a/daemon/ui/testdata/orig-default-config.json b/daemon/ui/testdata/orig-default-config.json new file mode 100644 index 0000000..4c461a7 --- /dev/null +++ b/daemon/ui/testdata/orig-default-config.json @@ -0,0 +1,20 @@ +{ + "Server": + { + "Address":"unix:///tmp/osui.sock", + "LogFile":"/var/log/opensnitchd.log" + }, + "DefaultAction": "allow", + "DefaultDuration": "once", + "InterceptUnknown": false, + "ProcMonitorMethod": "ebpf", + "LogLevel": 2, + "LogUTC": true, + "LogMicro": false, + "Firewall": "nftables", + "Stats": { + "MaxEvents": 150, + "MaxStats": 25, + "Workers": 6 + } +} diff --git a/ebpf_prog/Makefile b/ebpf_prog/Makefile new file mode 100644 index 0000000..e92aeef --- /dev/null +++ b/ebpf_prog/Makefile @@ -0,0 +1,59 @@ +# OpenSnitch - 2023 +# +# On Debian based distros we need the following 2 directories. +# Otherwise, just use the kernel headers from the kernel sources. +# +KERNEL_DIR ?= /lib/modules/$(shell uname -r)/source +KERNEL_HEADERS ?= /usr/src/linux-headers-$(shell uname -r)/ +CLANG ?= clang +LLC ?= llc +LLVM_STRIP ?= llvm-strip -g +ARCH ?= $(shell uname -m) + +# as in /usr/src/linux-headers-*/arch/ +# TODO: extract correctly the archs, and add more if needed. +ifeq ($(ARCH),x86_64) + ARCH := x86 +else ifeq ($(ARCH),i686) + ARCH := x86 +else ifeq ($(ARCH),armv7l) + ARCH := arm +else ifeq ($(ARCH),aarch64) + ARCH := arm64 +endif + +ifeq ($(ARCH),arm) + # on previous archs, it fails with "SMP not supported on pre-ARMv6" + EXTRA_FLAGS = "-D__LINUX_ARM_ARCH__=7" +endif + +BIN := opensnitch.o opensnitch-procs.o opensnitch-dns.o +CLANG_FLAGS = -I. \ + -I$(KERNEL_HEADERS)/arch/$(ARCH)/include/generated/ \ + -I$(KERNEL_HEADERS)/include \ + -include $(KERNEL_DIR)/include/linux/kconfig.h \ + -I$(KERNEL_DIR)/include \ + -I$(KERNEL_DIR)/include/uapi \ + -I$(KERNEL_DIR)/include/generated/uapi \ + -I$(KERNEL_DIR)/arch/$(ARCH)/include \ + -I$(KERNEL_DIR)/arch/$(ARCH)/include/generated \ + -I$(KERNEL_DIR)/arch/$(ARCH)/include/uapi \ + -I$(KERNEL_DIR)/arch/$(ARCH)/include/generated/uapi \ + -I$(KERNEL_DIR)/tools/testing/selftests/bpf/ \ + -D__KERNEL__ -D__BPF_TRACING__ -Wno-unused-value -Wno-pointer-sign \ + -D__TARGET_ARCH_$(ARCH) -Wno-compare-distinct-pointer-types \ + $(EXTRA_FLAGS) \ + -Wno-gnu-variable-sized-type-not-at-end \ + -Wno-address-of-packed-member -Wno-tautological-compare \ + -Wno-unknown-warning-option \ + -g -O2 -emit-llvm + +all: $(BIN) + +%.o: %.c + $(CLANG) $(CLANG_FLAGS) -c $< -o $@.partial + $(LLC) -march=bpf -mcpu=generic -filetype=obj -o $@ $@.partial + rm -f $@.partial + +clean: + rm -f *.o *.partial diff --git a/ebpf_prog/README b/ebpf_prog/README new file mode 100644 index 0000000..8cc55b4 --- /dev/null +++ b/ebpf_prog/README @@ -0,0 +1,72 @@ +Compilation requires getting kernel sources for now. + +There's a helper script to automate this process: + https://github.com/evilsocket/opensnitch/blob/master/utils/packaging/build_modules.sh + +The basic steps to compile the modules are: + + sudo apt install clang llvm libelf-dev libzip-dev flex bison libssl-dev bc rsync python3 + cd opensnitch + wget https://github.com/torvalds/linux/archive/v5.8.tar.gz + tar -xf v5.8.tar.gz + cp ebpf_prog/opensnitch*.c ebpf_prog/common* ebpf_prog/Makefile linux-5.8/samples/bpf/ + cp -r ebpf_prog/bpf_headers/ linux-5.8/samples/bpf/ + cd linux-5.8 && yes "" | make oldconfig && make prepare && make headers_install # (1 min) + cd samples/bpf && make KERNEL_DIR=../../linux-5.8/ + objdump -h opensnitch.o # you should see many sections, number 1 should be called kprobe/tcp_v4_connect + llvm-strip -g opensnitch*.o # remove debug info + sudo cp opensnitch*.o /usr/lib/opensnitchd/ebpf/ # or /etc/opensnitchd for < v1.6.x + cd ../../../daemon + +Since v1.6.0, opensnitchd expects to find the opensnitch*.o modules under: + /usr/local/lib/opensnitchd/ebpf/ + /usr/lib/opensnitchd/ebpf/ + /etc/opensnitchd/ # deprecated, only on < v1.5.x + +start opensnitchd with: + + opensnitchd -rules-path /etc/opensnitchd/rules -process-monitor-method ebpf + +--- + +### Compiling for Fedora (and others rpm based systems) + +You need to install the kernel-devel, clang and llvm packages. + +Then: `cd ebpf_prog/ ; make KERNEL_DIR=/usr/src/kernels/$(uname -r)/` + +(or just pass the kernel version you want) + +### Notes + +The kernel where you intend to run it must have some options activated: + + $ grep BPF /boot/config-$(uname -r) + CONFIG_CGROUP_BPF=y + CONFIG_BPF=y + CONFIG_BPF_SYSCALL=y + CONFIG_BPF_EVENTS=y + CONFIG_KPROBES=y + CONFIG_KPROBE_EVENTS=y + +For the opensnitch-procs.o module to work, this option must be enabled: + + $ grep FTRACE_SYSCALLS /boot/config-$(uname -r) + CONFIG_FTRACE_SYSCALLS=y + +(https://github.com/iovisor/bcc/blob/master/docs/kernel_config.md) + +Also, in some distributions debugfs is not mounted automatically. +Since v1.6.0 we try to mount it automatically. If you're running +a lower version so you'll need to mount it manually: + + $ sudo mount -t debugfs none /sys/kernel/debug + +In order to make it permanent add it to /etc/fstab: + +debugfs /sys/kernel/debug debugfs defaults 0 0 + + +opensnitch-procs.o and opensnitch-dns.o are only compatible with kernels >= 5.5, +bpf_probe_read_user*() were added on that kernel on: +https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md#helpers diff --git a/ebpf_prog/arm-clang-asm-fix.patch b/ebpf_prog/arm-clang-asm-fix.patch new file mode 100644 index 0000000..d8dd394 --- /dev/null +++ b/ebpf_prog/arm-clang-asm-fix.patch @@ -0,0 +1,14 @@ +--- ../../arch/arm/include/asm/unified.h 2021-04-20 10:47:54.075834124 +0000 ++++ ../../arch/arm/include/asm/unified-clang-fix.h 2021-04-20 10:47:38.943811970 +0000 +@@ -11,7 +11,10 @@ + #if defined(__ASSEMBLY__) + .syntax unified + #else +-__asm__(".syntax unified"); ++//__asm__(".syntax unified"); ++#ifndef __clang__ ++ __asm__(".syntax unified"); ++#endif + #endif + + #ifdef CONFIG_CPU_V7M diff --git a/ebpf_prog/bpf_headers/bpf_core_read.h b/ebpf_prog/bpf_headers/bpf_core_read.h new file mode 100644 index 0000000..496e6a8 --- /dev/null +++ b/ebpf_prog/bpf_headers/bpf_core_read.h @@ -0,0 +1,484 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ +#ifndef __BPF_CORE_READ_H__ +#define __BPF_CORE_READ_H__ + +/* + * enum bpf_field_info_kind is passed as a second argument into + * __builtin_preserve_field_info() built-in to get a specific aspect of + * a field, captured as a first argument. __builtin_preserve_field_info(field, + * info_kind) returns __u32 integer and produces BTF field relocation, which + * is understood and processed by libbpf during BPF object loading. See + * selftests/bpf for examples. + */ +enum bpf_field_info_kind { + BPF_FIELD_BYTE_OFFSET = 0, /* field byte offset */ + BPF_FIELD_BYTE_SIZE = 1, + BPF_FIELD_EXISTS = 2, /* field existence in target kernel */ + BPF_FIELD_SIGNED = 3, + BPF_FIELD_LSHIFT_U64 = 4, + BPF_FIELD_RSHIFT_U64 = 5, +}; + +/* second argument to __builtin_btf_type_id() built-in */ +enum bpf_type_id_kind { + BPF_TYPE_ID_LOCAL = 0, /* BTF type ID in local program */ + BPF_TYPE_ID_TARGET = 1, /* BTF type ID in target kernel */ +}; + +/* second argument to __builtin_preserve_type_info() built-in */ +enum bpf_type_info_kind { + BPF_TYPE_EXISTS = 0, /* type existence in target kernel */ + BPF_TYPE_SIZE = 1, /* type size in target kernel */ + BPF_TYPE_MATCHES = 2, /* type match in target kernel */ +}; + +/* second argument to __builtin_preserve_enum_value() built-in */ +enum bpf_enum_value_kind { + BPF_ENUMVAL_EXISTS = 0, /* enum value existence in kernel */ + BPF_ENUMVAL_VALUE = 1, /* enum value value relocation */ +}; + +#define __CORE_RELO(src, field, info) \ + __builtin_preserve_field_info((src)->field, BPF_FIELD_##info) + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define __CORE_BITFIELD_PROBE_READ(dst, src, fld) \ + bpf_probe_read_kernel( \ + (void *)dst, \ + __CORE_RELO(src, fld, BYTE_SIZE), \ + (const void *)src + __CORE_RELO(src, fld, BYTE_OFFSET)) +#else +/* semantics of LSHIFT_64 assumes loading values into low-ordered bytes, so + * for big-endian we need to adjust destination pointer accordingly, based on + * field byte size + */ +#define __CORE_BITFIELD_PROBE_READ(dst, src, fld) \ + bpf_probe_read_kernel( \ + (void *)dst + (8 - __CORE_RELO(src, fld, BYTE_SIZE)), \ + __CORE_RELO(src, fld, BYTE_SIZE), \ + (const void *)src + __CORE_RELO(src, fld, BYTE_OFFSET)) +#endif + +/* + * Extract bitfield, identified by s->field, and return its value as u64. + * All this is done in relocatable manner, so bitfield changes such as + * signedness, bit size, offset changes, this will be handled automatically. + * This version of macro is using bpf_probe_read_kernel() to read underlying + * integer storage. Macro functions as an expression and its return type is + * bpf_probe_read_kernel()'s return value: 0, on success, <0 on error. + */ +#define BPF_CORE_READ_BITFIELD_PROBED(s, field) ({ \ + unsigned long long val = 0; \ + \ + __CORE_BITFIELD_PROBE_READ(&val, s, field); \ + val <<= __CORE_RELO(s, field, LSHIFT_U64); \ + if (__CORE_RELO(s, field, SIGNED)) \ + val = ((long long)val) >> __CORE_RELO(s, field, RSHIFT_U64); \ + else \ + val = val >> __CORE_RELO(s, field, RSHIFT_U64); \ + val; \ +}) + +/* + * Extract bitfield, identified by s->field, and return its value as u64. + * This version of macro is using direct memory reads and should be used from + * BPF program types that support such functionality (e.g., typed raw + * tracepoints). + */ +#define BPF_CORE_READ_BITFIELD(s, field) ({ \ + const void *p = (const void *)s + __CORE_RELO(s, field, BYTE_OFFSET); \ + unsigned long long val; \ + \ + /* This is a so-called barrier_var() operation that makes specified \ + * variable "a black box" for optimizing compiler. \ + * It forces compiler to perform BYTE_OFFSET relocation on p and use \ + * its calculated value in the switch below, instead of applying \ + * the same relocation 4 times for each individual memory load. \ + */ \ + asm volatile("" : "=r"(p) : "0"(p)); \ + \ + switch (__CORE_RELO(s, field, BYTE_SIZE)) { \ + case 1: val = *(const unsigned char *)p; break; \ + case 2: val = *(const unsigned short *)p; break; \ + case 4: val = *(const unsigned int *)p; break; \ + case 8: val = *(const unsigned long long *)p; break; \ + } \ + val <<= __CORE_RELO(s, field, LSHIFT_U64); \ + if (__CORE_RELO(s, field, SIGNED)) \ + val = ((long long)val) >> __CORE_RELO(s, field, RSHIFT_U64); \ + else \ + val = val >> __CORE_RELO(s, field, RSHIFT_U64); \ + val; \ +}) + +#define ___bpf_field_ref1(field) (field) +#define ___bpf_field_ref2(type, field) (((typeof(type) *)0)->field) +#define ___bpf_field_ref(args...) \ + ___bpf_apply(___bpf_field_ref, ___bpf_narg(args))(args) + +/* + * Convenience macro to check that field actually exists in target kernel's. + * Returns: + * 1, if matching field is present in target kernel; + * 0, if no matching field found. + * + * Supports two forms: + * - field reference through variable access: + * bpf_core_field_exists(p->my_field); + * - field reference through type and field names: + * bpf_core_field_exists(struct my_type, my_field). + */ +#define bpf_core_field_exists(field...) \ + __builtin_preserve_field_info(___bpf_field_ref(field), BPF_FIELD_EXISTS) + +/* + * Convenience macro to get the byte size of a field. Works for integers, + * struct/unions, pointers, arrays, and enums. + * + * Supports two forms: + * - field reference through variable access: + * bpf_core_field_size(p->my_field); + * - field reference through type and field names: + * bpf_core_field_size(struct my_type, my_field). + */ +#define bpf_core_field_size(field...) \ + __builtin_preserve_field_info(___bpf_field_ref(field), BPF_FIELD_BYTE_SIZE) + +/* + * Convenience macro to get field's byte offset. + * + * Supports two forms: + * - field reference through variable access: + * bpf_core_field_offset(p->my_field); + * - field reference through type and field names: + * bpf_core_field_offset(struct my_type, my_field). + */ +#define bpf_core_field_offset(field...) \ + __builtin_preserve_field_info(___bpf_field_ref(field), BPF_FIELD_BYTE_OFFSET) + +/* + * Convenience macro to get BTF type ID of a specified type, using a local BTF + * information. Return 32-bit unsigned integer with type ID from program's own + * BTF. Always succeeds. + */ +#define bpf_core_type_id_local(type) \ + __builtin_btf_type_id(*(typeof(type) *)0, BPF_TYPE_ID_LOCAL) + +/* + * Convenience macro to get BTF type ID of a target kernel's type that matches + * specified local type. + * Returns: + * - valid 32-bit unsigned type ID in kernel BTF; + * - 0, if no matching type was found in a target kernel BTF. + */ +#define bpf_core_type_id_kernel(type) \ + __builtin_btf_type_id(*(typeof(type) *)0, BPF_TYPE_ID_TARGET) + +/* + * Convenience macro to check that provided named type + * (struct/union/enum/typedef) exists in a target kernel. + * Returns: + * 1, if such type is present in target kernel's BTF; + * 0, if no matching type is found. + */ +#define bpf_core_type_exists(type) \ + __builtin_preserve_type_info(*(typeof(type) *)0, BPF_TYPE_EXISTS) + +/* + * Convenience macro to check that provided named type + * (struct/union/enum/typedef) "matches" that in a target kernel. + * Returns: + * 1, if the type matches in the target kernel's BTF; + * 0, if the type does not match any in the target kernel + */ +#define bpf_core_type_matches(type) \ + __builtin_preserve_type_info(*(typeof(type) *)0, BPF_TYPE_MATCHES) + +/* + * Convenience macro to get the byte size of a provided named type + * (struct/union/enum/typedef) in a target kernel. + * Returns: + * >= 0 size (in bytes), if type is present in target kernel's BTF; + * 0, if no matching type is found. + */ +#define bpf_core_type_size(type) \ + __builtin_preserve_type_info(*(typeof(type) *)0, BPF_TYPE_SIZE) + +/* + * Convenience macro to check that provided enumerator value is defined in + * a target kernel. + * Returns: + * 1, if specified enum type and its enumerator value are present in target + * kernel's BTF; + * 0, if no matching enum and/or enum value within that enum is found. + */ +#define bpf_core_enum_value_exists(enum_type, enum_value) \ + __builtin_preserve_enum_value(*(typeof(enum_type) *)enum_value, BPF_ENUMVAL_EXISTS) + +/* + * Convenience macro to get the integer value of an enumerator value in + * a target kernel. + * Returns: + * 64-bit value, if specified enum type and its enumerator value are + * present in target kernel's BTF; + * 0, if no matching enum and/or enum value within that enum is found. + */ +#define bpf_core_enum_value(enum_type, enum_value) \ + __builtin_preserve_enum_value(*(typeof(enum_type) *)enum_value, BPF_ENUMVAL_VALUE) + +/* + * bpf_core_read() abstracts away bpf_probe_read_kernel() call and captures + * offset relocation for source address using __builtin_preserve_access_index() + * built-in, provided by Clang. + * + * __builtin_preserve_access_index() takes as an argument an expression of + * taking an address of a field within struct/union. It makes compiler emit + * a relocation, which records BTF type ID describing root struct/union and an + * accessor string which describes exact embedded field that was used to take + * an address. See detailed description of this relocation format and + * semantics in comments to struct bpf_field_reloc in libbpf_internal.h. + * + * This relocation allows libbpf to adjust BPF instruction to use correct + * actual field offset, based on target kernel BTF type that matches original + * (local) BTF, used to record relocation. + */ +#define bpf_core_read(dst, sz, src) \ + bpf_probe_read_kernel(dst, sz, (const void *)__builtin_preserve_access_index(src)) + +/* NOTE: see comments for BPF_CORE_READ_USER() about the proper types use. */ +#define bpf_core_read_user(dst, sz, src) \ + bpf_probe_read_user(dst, sz, (const void *)__builtin_preserve_access_index(src)) +/* + * bpf_core_read_str() is a thin wrapper around bpf_probe_read_str() + * additionally emitting BPF CO-RE field relocation for specified source + * argument. + */ +#define bpf_core_read_str(dst, sz, src) \ + bpf_probe_read_kernel_str(dst, sz, (const void *)__builtin_preserve_access_index(src)) + +/* NOTE: see comments for BPF_CORE_READ_USER() about the proper types use. */ +#define bpf_core_read_user_str(dst, sz, src) \ + bpf_probe_read_user_str(dst, sz, (const void *)__builtin_preserve_access_index(src)) + +#define ___concat(a, b) a ## b +#define ___apply(fn, n) ___concat(fn, n) +#define ___nth(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, __11, N, ...) N + +/* + * return number of provided arguments; used for switch-based variadic macro + * definitions (see ___last, ___arrow, etc below) + */ +#define ___narg(...) ___nth(_, ##__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +/* + * return 0 if no arguments are passed, N - otherwise; used for + * recursively-defined macros to specify termination (0) case, and generic + * (N) case (e.g., ___read_ptrs, ___core_read) + */ +#define ___empty(...) ___nth(_, ##__VA_ARGS__, N, N, N, N, N, N, N, N, N, N, 0) + +#define ___last1(x) x +#define ___last2(a, x) x +#define ___last3(a, b, x) x +#define ___last4(a, b, c, x) x +#define ___last5(a, b, c, d, x) x +#define ___last6(a, b, c, d, e, x) x +#define ___last7(a, b, c, d, e, f, x) x +#define ___last8(a, b, c, d, e, f, g, x) x +#define ___last9(a, b, c, d, e, f, g, h, x) x +#define ___last10(a, b, c, d, e, f, g, h, i, x) x +#define ___last(...) ___apply(___last, ___narg(__VA_ARGS__))(__VA_ARGS__) + +#define ___nolast2(a, _) a +#define ___nolast3(a, b, _) a, b +#define ___nolast4(a, b, c, _) a, b, c +#define ___nolast5(a, b, c, d, _) a, b, c, d +#define ___nolast6(a, b, c, d, e, _) a, b, c, d, e +#define ___nolast7(a, b, c, d, e, f, _) a, b, c, d, e, f +#define ___nolast8(a, b, c, d, e, f, g, _) a, b, c, d, e, f, g +#define ___nolast9(a, b, c, d, e, f, g, h, _) a, b, c, d, e, f, g, h +#define ___nolast10(a, b, c, d, e, f, g, h, i, _) a, b, c, d, e, f, g, h, i +#define ___nolast(...) ___apply(___nolast, ___narg(__VA_ARGS__))(__VA_ARGS__) + +#define ___arrow1(a) a +#define ___arrow2(a, b) a->b +#define ___arrow3(a, b, c) a->b->c +#define ___arrow4(a, b, c, d) a->b->c->d +#define ___arrow5(a, b, c, d, e) a->b->c->d->e +#define ___arrow6(a, b, c, d, e, f) a->b->c->d->e->f +#define ___arrow7(a, b, c, d, e, f, g) a->b->c->d->e->f->g +#define ___arrow8(a, b, c, d, e, f, g, h) a->b->c->d->e->f->g->h +#define ___arrow9(a, b, c, d, e, f, g, h, i) a->b->c->d->e->f->g->h->i +#define ___arrow10(a, b, c, d, e, f, g, h, i, j) a->b->c->d->e->f->g->h->i->j +#define ___arrow(...) ___apply(___arrow, ___narg(__VA_ARGS__))(__VA_ARGS__) + +#define ___type(...) typeof(___arrow(__VA_ARGS__)) + +#define ___read(read_fn, dst, src_type, src, accessor) \ + read_fn((void *)(dst), sizeof(*(dst)), &((src_type)(src))->accessor) + +/* "recursively" read a sequence of inner pointers using local __t var */ +#define ___rd_first(fn, src, a) ___read(fn, &__t, ___type(src), src, a); +#define ___rd_last(fn, ...) \ + ___read(fn, &__t, ___type(___nolast(__VA_ARGS__)), __t, ___last(__VA_ARGS__)); +#define ___rd_p1(fn, ...) const void *__t; ___rd_first(fn, __VA_ARGS__) +#define ___rd_p2(fn, ...) ___rd_p1(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) +#define ___rd_p3(fn, ...) ___rd_p2(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) +#define ___rd_p4(fn, ...) ___rd_p3(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) +#define ___rd_p5(fn, ...) ___rd_p4(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) +#define ___rd_p6(fn, ...) ___rd_p5(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) +#define ___rd_p7(fn, ...) ___rd_p6(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) +#define ___rd_p8(fn, ...) ___rd_p7(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) +#define ___rd_p9(fn, ...) ___rd_p8(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) +#define ___read_ptrs(fn, src, ...) \ + ___apply(___rd_p, ___narg(__VA_ARGS__))(fn, src, __VA_ARGS__) + +#define ___core_read0(fn, fn_ptr, dst, src, a) \ + ___read(fn, dst, ___type(src), src, a); +#define ___core_readN(fn, fn_ptr, dst, src, ...) \ + ___read_ptrs(fn_ptr, src, ___nolast(__VA_ARGS__)) \ + ___read(fn, dst, ___type(src, ___nolast(__VA_ARGS__)), __t, \ + ___last(__VA_ARGS__)); +#define ___core_read(fn, fn_ptr, dst, src, a, ...) \ + ___apply(___core_read, ___empty(__VA_ARGS__))(fn, fn_ptr, dst, \ + src, a, ##__VA_ARGS__) + +/* + * BPF_CORE_READ_INTO() is a more performance-conscious variant of + * BPF_CORE_READ(), in which final field is read into user-provided storage. + * See BPF_CORE_READ() below for more details on general usage. + */ +#define BPF_CORE_READ_INTO(dst, src, a, ...) ({ \ + ___core_read(bpf_core_read, bpf_core_read, \ + dst, (src), a, ##__VA_ARGS__) \ +}) + +/* + * Variant of BPF_CORE_READ_INTO() for reading from user-space memory. + * + * NOTE: see comments for BPF_CORE_READ_USER() about the proper types use. + */ +#define BPF_CORE_READ_USER_INTO(dst, src, a, ...) ({ \ + ___core_read(bpf_core_read_user, bpf_core_read_user, \ + dst, (src), a, ##__VA_ARGS__) \ +}) + +/* Non-CO-RE variant of BPF_CORE_READ_INTO() */ +#define BPF_PROBE_READ_INTO(dst, src, a, ...) ({ \ + ___core_read(bpf_probe_read, bpf_probe_read, \ + dst, (src), a, ##__VA_ARGS__) \ +}) + +/* Non-CO-RE variant of BPF_CORE_READ_USER_INTO(). + * + * As no CO-RE relocations are emitted, source types can be arbitrary and are + * not restricted to kernel types only. + */ +#define BPF_PROBE_READ_USER_INTO(dst, src, a, ...) ({ \ + ___core_read(bpf_probe_read_user, bpf_probe_read_user, \ + dst, (src), a, ##__VA_ARGS__) \ +}) + +/* + * BPF_CORE_READ_STR_INTO() does same "pointer chasing" as + * BPF_CORE_READ() for intermediate pointers, but then executes (and returns + * corresponding error code) bpf_core_read_str() for final string read. + */ +#define BPF_CORE_READ_STR_INTO(dst, src, a, ...) ({ \ + ___core_read(bpf_core_read_str, bpf_core_read, \ + dst, (src), a, ##__VA_ARGS__) \ +}) + +/* + * Variant of BPF_CORE_READ_STR_INTO() for reading from user-space memory. + * + * NOTE: see comments for BPF_CORE_READ_USER() about the proper types use. + */ +#define BPF_CORE_READ_USER_STR_INTO(dst, src, a, ...) ({ \ + ___core_read(bpf_core_read_user_str, bpf_core_read_user, \ + dst, (src), a, ##__VA_ARGS__) \ +}) + +/* Non-CO-RE variant of BPF_CORE_READ_STR_INTO() */ +#define BPF_PROBE_READ_STR_INTO(dst, src, a, ...) ({ \ + ___core_read(bpf_probe_read_str, bpf_probe_read, \ + dst, (src), a, ##__VA_ARGS__) \ +}) + +/* + * Non-CO-RE variant of BPF_CORE_READ_USER_STR_INTO(). + * + * As no CO-RE relocations are emitted, source types can be arbitrary and are + * not restricted to kernel types only. + */ +#define BPF_PROBE_READ_USER_STR_INTO(dst, src, a, ...) ({ \ + ___core_read(bpf_probe_read_user_str, bpf_probe_read_user, \ + dst, (src), a, ##__VA_ARGS__) \ +}) + +/* + * BPF_CORE_READ() is used to simplify BPF CO-RE relocatable read, especially + * when there are few pointer chasing steps. + * E.g., what in non-BPF world (or in BPF w/ BCC) would be something like: + * int x = s->a.b.c->d.e->f->g; + * can be succinctly achieved using BPF_CORE_READ as: + * int x = BPF_CORE_READ(s, a.b.c, d.e, f, g); + * + * BPF_CORE_READ will decompose above statement into 4 bpf_core_read (BPF + * CO-RE relocatable bpf_probe_read_kernel() wrapper) calls, logically + * equivalent to: + * 1. const void *__t = s->a.b.c; + * 2. __t = __t->d.e; + * 3. __t = __t->f; + * 4. return __t->g; + * + * Equivalence is logical, because there is a heavy type casting/preservation + * involved, as well as all the reads are happening through + * bpf_probe_read_kernel() calls using __builtin_preserve_access_index() to + * emit CO-RE relocations. + * + * N.B. Only up to 9 "field accessors" are supported, which should be more + * than enough for any practical purpose. + */ +#define BPF_CORE_READ(src, a, ...) ({ \ + ___type((src), a, ##__VA_ARGS__) __r; \ + BPF_CORE_READ_INTO(&__r, (src), a, ##__VA_ARGS__); \ + __r; \ +}) + +/* + * Variant of BPF_CORE_READ() for reading from user-space memory. + * + * NOTE: all the source types involved are still *kernel types* and need to + * exist in kernel (or kernel module) BTF, otherwise CO-RE relocation will + * fail. Custom user types are not relocatable with CO-RE. + * The typical situation in which BPF_CORE_READ_USER() might be used is to + * read kernel UAPI types from the user-space memory passed in as a syscall + * input argument. + */ +#define BPF_CORE_READ_USER(src, a, ...) ({ \ + ___type((src), a, ##__VA_ARGS__) __r; \ + BPF_CORE_READ_USER_INTO(&__r, (src), a, ##__VA_ARGS__); \ + __r; \ +}) + +/* Non-CO-RE variant of BPF_CORE_READ() */ +#define BPF_PROBE_READ(src, a, ...) ({ \ + ___type((src), a, ##__VA_ARGS__) __r; \ + BPF_PROBE_READ_INTO(&__r, (src), a, ##__VA_ARGS__); \ + __r; \ +}) + +/* + * Non-CO-RE variant of BPF_CORE_READ_USER(). + * + * As no CO-RE relocations are emitted, source types can be arbitrary and are + * not restricted to kernel types only. + */ +#define BPF_PROBE_READ_USER(src, a, ...) ({ \ + ___type((src), a, ##__VA_ARGS__) __r; \ + BPF_PROBE_READ_USER_INTO(&__r, (src), a, ##__VA_ARGS__); \ + __r; \ +}) + +#endif + diff --git a/ebpf_prog/bpf_headers/bpf_helper_defs.h b/ebpf_prog/bpf_headers/bpf_helper_defs.h new file mode 100644 index 0000000..0916f7b --- /dev/null +++ b/ebpf_prog/bpf_headers/bpf_helper_defs.h @@ -0,0 +1,4582 @@ +/* This is auto-generated file. See bpf_doc.py for details. */ + +/* Forward declarations of BPF structs */ +struct bpf_fib_lookup; +struct bpf_sk_lookup; +struct bpf_perf_event_data; +struct bpf_perf_event_value; +struct bpf_pidns_info; +struct bpf_redir_neigh; +struct bpf_sock; +struct bpf_sock_addr; +struct bpf_sock_ops; +struct bpf_sock_tuple; +struct bpf_spin_lock; +struct bpf_sysctl; +struct bpf_tcp_sock; +struct bpf_tunnel_key; +struct bpf_xfrm_state; +struct linux_binprm; +struct pt_regs; +struct sk_reuseport_md; +struct sockaddr; +struct tcphdr; +struct seq_file; +struct tcp6_sock; +struct tcp_sock; +struct tcp_timewait_sock; +struct tcp_request_sock; +struct udp6_sock; +struct unix_sock; +struct task_struct; +struct __sk_buff; +struct sk_msg_md; +struct xdp_md; +struct path; +struct btf_ptr; +struct inode; +struct socket; +struct file; +struct bpf_timer; +struct mptcp_sock; +struct bpf_dynptr; +struct iphdr; +struct ipv6hdr; + +/* + * bpf_map_lookup_elem + * + * Perform a lookup in *map* for an entry associated to *key*. + * + * Returns + * Map value associated to *key*, or **NULL** if no entry was + * found. + */ +static void *(*bpf_map_lookup_elem)(void *map, const void *key) = (void *) 1; + +/* + * bpf_map_update_elem + * + * Add or update the value of the entry associated to *key* in + * *map* with *value*. *flags* is one of: + * + * **BPF_NOEXIST** + * The entry for *key* must not exist in the map. + * **BPF_EXIST** + * The entry for *key* must already exist in the map. + * **BPF_ANY** + * No condition on the existence of the entry for *key*. + * + * Flag value **BPF_NOEXIST** cannot be used for maps of types + * **BPF_MAP_TYPE_ARRAY** or **BPF_MAP_TYPE_PERCPU_ARRAY** (all + * elements always exist), the helper would return an error. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_map_update_elem)(void *map, const void *key, const void *value, __u64 flags) = (void *) 2; + +/* + * bpf_map_delete_elem + * + * Delete entry with *key* from *map*. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_map_delete_elem)(void *map, const void *key) = (void *) 3; + +/* + * bpf_probe_read + * + * For tracing programs, safely attempt to read *size* bytes from + * kernel space address *unsafe_ptr* and store the data in *dst*. + * + * Generally, use **bpf_probe_read_user**\ () or + * **bpf_probe_read_kernel**\ () instead. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_probe_read)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 4; + +/* + * bpf_ktime_get_ns + * + * Return the time elapsed since system boot, in nanoseconds. + * Does not include time the system was suspended. + * See: **clock_gettime**\ (**CLOCK_MONOTONIC**) + * + * Returns + * Current *ktime*. + */ +static __u64 (*bpf_ktime_get_ns)(void) = (void *) 5; + +/* + * bpf_trace_printk + * + * This helper is a "printk()-like" facility for debugging. It + * prints a message defined by format *fmt* (of size *fmt_size*) + * to file *\/sys/kernel/debug/tracing/trace* from DebugFS, if + * available. It can take up to three additional **u64** + * arguments (as an eBPF helpers, the total number of arguments is + * limited to five). + * + * Each time the helper is called, it appends a line to the trace. + * Lines are discarded while *\/sys/kernel/debug/tracing/trace* is + * open, use *\/sys/kernel/debug/tracing/trace_pipe* to avoid this. + * The format of the trace is customizable, and the exact output + * one will get depends on the options set in + * *\/sys/kernel/debug/tracing/trace_options* (see also the + * *README* file under the same directory). However, it usually + * defaults to something like: + * + * :: + * + * telnet-470 [001] .N.. 419421.045894: 0x00000001: <formatted msg> + * + * In the above: + * + * * ``telnet`` is the name of the current task. + * * ``470`` is the PID of the current task. + * * ``001`` is the CPU number on which the task is + * running. + * * In ``.N..``, each character refers to a set of + * options (whether irqs are enabled, scheduling + * options, whether hard/softirqs are running, level of + * preempt_disabled respectively). **N** means that + * **TIF_NEED_RESCHED** and **PREEMPT_NEED_RESCHED** + * are set. + * * ``419421.045894`` is a timestamp. + * * ``0x00000001`` is a fake value used by BPF for the + * instruction pointer register. + * * ``<formatted msg>`` is the message formatted with + * *fmt*. + * + * The conversion specifiers supported by *fmt* are similar, but + * more limited than for printk(). They are **%d**, **%i**, + * **%u**, **%x**, **%ld**, **%li**, **%lu**, **%lx**, **%lld**, + * **%lli**, **%llu**, **%llx**, **%p**, **%s**. No modifier (size + * of field, padding with zeroes, etc.) is available, and the + * helper will return **-EINVAL** (but print nothing) if it + * encounters an unknown specifier. + * + * Also, note that **bpf_trace_printk**\ () is slow, and should + * only be used for debugging purposes. For this reason, a notice + * block (spanning several lines) is printed to kernel logs and + * states that the helper should not be used "for production use" + * the first time this helper is used (or more precisely, when + * **trace_printk**\ () buffers are allocated). For passing values + * to user space, perf events should be preferred. + * + * Returns + * The number of bytes written to the buffer, or a negative error + * in case of failure. + */ +static long (*bpf_trace_printk)(const char *fmt, __u32 fmt_size, ...) = (void *) 6; + +/* + * bpf_get_prandom_u32 + * + * Get a pseudo-random number. + * + * From a security point of view, this helper uses its own + * pseudo-random internal state, and cannot be used to infer the + * seed of other random functions in the kernel. However, it is + * essential to note that the generator used by the helper is not + * cryptographically secure. + * + * Returns + * A random 32-bit unsigned value. + */ +static __u32 (*bpf_get_prandom_u32)(void) = (void *) 7; + +/* + * bpf_get_smp_processor_id + * + * Get the SMP (symmetric multiprocessing) processor id. Note that + * all programs run with migration disabled, which means that the + * SMP processor id is stable during all the execution of the + * program. + * + * Returns + * The SMP id of the processor running the program. + */ +static __u32 (*bpf_get_smp_processor_id)(void) = (void *) 8; + +/* + * bpf_skb_store_bytes + * + * Store *len* bytes from address *from* into the packet + * associated to *skb*, at *offset*. *flags* are a combination of + * **BPF_F_RECOMPUTE_CSUM** (automatically recompute the + * checksum for the packet after storing the bytes) and + * **BPF_F_INVALIDATE_HASH** (set *skb*\ **->hash**, *skb*\ + * **->swhash** and *skb*\ **->l4hash** to 0). + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_store_bytes)(struct __sk_buff *skb, __u32 offset, const void *from, __u32 len, __u64 flags) = (void *) 9; + +/* + * bpf_l3_csum_replace + * + * Recompute the layer 3 (e.g. IP) checksum for the packet + * associated to *skb*. Computation is incremental, so the helper + * must know the former value of the header field that was + * modified (*from*), the new value of this field (*to*), and the + * number of bytes (2 or 4) for this field, stored in *size*. + * Alternatively, it is possible to store the difference between + * the previous and the new values of the header field in *to*, by + * setting *from* and *size* to 0. For both methods, *offset* + * indicates the location of the IP checksum within the packet. + * + * This helper works in combination with **bpf_csum_diff**\ (), + * which does not update the checksum in-place, but offers more + * flexibility and can handle sizes larger than 2 or 4 for the + * checksum to update. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_l3_csum_replace)(struct __sk_buff *skb, __u32 offset, __u64 from, __u64 to, __u64 size) = (void *) 10; + +/* + * bpf_l4_csum_replace + * + * Recompute the layer 4 (e.g. TCP, UDP or ICMP) checksum for the + * packet associated to *skb*. Computation is incremental, so the + * helper must know the former value of the header field that was + * modified (*from*), the new value of this field (*to*), and the + * number of bytes (2 or 4) for this field, stored on the lowest + * four bits of *flags*. Alternatively, it is possible to store + * the difference between the previous and the new values of the + * header field in *to*, by setting *from* and the four lowest + * bits of *flags* to 0. For both methods, *offset* indicates the + * location of the IP checksum within the packet. In addition to + * the size of the field, *flags* can be added (bitwise OR) actual + * flags. With **BPF_F_MARK_MANGLED_0**, a null checksum is left + * untouched (unless **BPF_F_MARK_ENFORCE** is added as well), and + * for updates resulting in a null checksum the value is set to + * **CSUM_MANGLED_0** instead. Flag **BPF_F_PSEUDO_HDR** indicates + * the checksum is to be computed against a pseudo-header. + * + * This helper works in combination with **bpf_csum_diff**\ (), + * which does not update the checksum in-place, but offers more + * flexibility and can handle sizes larger than 2 or 4 for the + * checksum to update. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_l4_csum_replace)(struct __sk_buff *skb, __u32 offset, __u64 from, __u64 to, __u64 flags) = (void *) 11; + +/* + * bpf_tail_call + * + * This special helper is used to trigger a "tail call", or in + * other words, to jump into another eBPF program. The same stack + * frame is used (but values on stack and in registers for the + * caller are not accessible to the callee). This mechanism allows + * for program chaining, either for raising the maximum number of + * available eBPF instructions, or to execute given programs in + * conditional blocks. For security reasons, there is an upper + * limit to the number of successive tail calls that can be + * performed. + * + * Upon call of this helper, the program attempts to jump into a + * program referenced at index *index* in *prog_array_map*, a + * special map of type **BPF_MAP_TYPE_PROG_ARRAY**, and passes + * *ctx*, a pointer to the context. + * + * If the call succeeds, the kernel immediately runs the first + * instruction of the new program. This is not a function call, + * and it never returns to the previous program. If the call + * fails, then the helper has no effect, and the caller continues + * to run its subsequent instructions. A call can fail if the + * destination program for the jump does not exist (i.e. *index* + * is superior to the number of entries in *prog_array_map*), or + * if the maximum number of tail calls has been reached for this + * chain of programs. This limit is defined in the kernel by the + * macro **MAX_TAIL_CALL_CNT** (not accessible to user space), + * which is currently set to 33. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_tail_call)(void *ctx, void *prog_array_map, __u32 index) = (void *) 12; + +/* + * bpf_clone_redirect + * + * Clone and redirect the packet associated to *skb* to another + * net device of index *ifindex*. Both ingress and egress + * interfaces can be used for redirection. The **BPF_F_INGRESS** + * value in *flags* is used to make the distinction (ingress path + * is selected if the flag is present, egress path otherwise). + * This is the only flag supported for now. + * + * In comparison with **bpf_redirect**\ () helper, + * **bpf_clone_redirect**\ () has the associated cost of + * duplicating the packet buffer, but this can be executed out of + * the eBPF program. Conversely, **bpf_redirect**\ () is more + * efficient, but it is handled through an action code where the + * redirection happens only after the eBPF program has returned. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_clone_redirect)(struct __sk_buff *skb, __u32 ifindex, __u64 flags) = (void *) 13; + +/* + * bpf_get_current_pid_tgid + * + * Get the current pid and tgid. + * + * Returns + * A 64-bit integer containing the current tgid and pid, and + * created as such: + * *current_task*\ **->tgid << 32 \|** + * *current_task*\ **->pid**. + */ +static __u64 (*bpf_get_current_pid_tgid)(void) = (void *) 14; + +/* + * bpf_get_current_uid_gid + * + * Get the current uid and gid. + * + * Returns + * A 64-bit integer containing the current GID and UID, and + * created as such: *current_gid* **<< 32 \|** *current_uid*. + */ +static __u64 (*bpf_get_current_uid_gid)(void) = (void *) 15; + +/* + * bpf_get_current_comm + * + * Copy the **comm** attribute of the current task into *buf* of + * *size_of_buf*. The **comm** attribute contains the name of + * the executable (excluding the path) for the current task. The + * *size_of_buf* must be strictly positive. On success, the + * helper makes sure that the *buf* is NUL-terminated. On failure, + * it is filled with zeroes. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_get_current_comm)(void *buf, __u32 size_of_buf) = (void *) 16; + +/* + * bpf_get_cgroup_classid + * + * Retrieve the classid for the current task, i.e. for the net_cls + * cgroup to which *skb* belongs. + * + * This helper can be used on TC egress path, but not on ingress. + * + * The net_cls cgroup provides an interface to tag network packets + * based on a user-provided identifier for all traffic coming from + * the tasks belonging to the related cgroup. See also the related + * kernel documentation, available from the Linux sources in file + * *Documentation/admin-guide/cgroup-v1/net_cls.rst*. + * + * The Linux kernel has two versions for cgroups: there are + * cgroups v1 and cgroups v2. Both are available to users, who can + * use a mixture of them, but note that the net_cls cgroup is for + * cgroup v1 only. This makes it incompatible with BPF programs + * run on cgroups, which is a cgroup-v2-only feature (a socket can + * only hold data for one version of cgroups at a time). + * + * This helper is only available is the kernel was compiled with + * the **CONFIG_CGROUP_NET_CLASSID** configuration option set to + * "**y**" or to "**m**". + * + * Returns + * The classid, or 0 for the default unconfigured classid. + */ +static __u32 (*bpf_get_cgroup_classid)(struct __sk_buff *skb) = (void *) 17; + +/* + * bpf_skb_vlan_push + * + * Push a *vlan_tci* (VLAN tag control information) of protocol + * *vlan_proto* to the packet associated to *skb*, then update + * the checksum. Note that if *vlan_proto* is different from + * **ETH_P_8021Q** and **ETH_P_8021AD**, it is considered to + * be **ETH_P_8021Q**. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_vlan_push)(struct __sk_buff *skb, __be16 vlan_proto, __u16 vlan_tci) = (void *) 18; + +/* + * bpf_skb_vlan_pop + * + * Pop a VLAN header from the packet associated to *skb*. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_vlan_pop)(struct __sk_buff *skb) = (void *) 19; + +/* + * bpf_skb_get_tunnel_key + * + * Get tunnel metadata. This helper takes a pointer *key* to an + * empty **struct bpf_tunnel_key** of **size**, that will be + * filled with tunnel metadata for the packet associated to *skb*. + * The *flags* can be set to **BPF_F_TUNINFO_IPV6**, which + * indicates that the tunnel is based on IPv6 protocol instead of + * IPv4. + * + * The **struct bpf_tunnel_key** is an object that generalizes the + * principal parameters used by various tunneling protocols into a + * single struct. This way, it can be used to easily make a + * decision based on the contents of the encapsulation header, + * "summarized" in this struct. In particular, it holds the IP + * address of the remote end (IPv4 or IPv6, depending on the case) + * in *key*\ **->remote_ipv4** or *key*\ **->remote_ipv6**. Also, + * this struct exposes the *key*\ **->tunnel_id**, which is + * generally mapped to a VNI (Virtual Network Identifier), making + * it programmable together with the **bpf_skb_set_tunnel_key**\ + * () helper. + * + * Let's imagine that the following code is part of a program + * attached to the TC ingress interface, on one end of a GRE + * tunnel, and is supposed to filter out all messages coming from + * remote ends with IPv4 address other than 10.0.0.1: + * + * :: + * + * int ret; + * struct bpf_tunnel_key key = {}; + * + * ret = bpf_skb_get_tunnel_key(skb, &key, sizeof(key), 0); + * if (ret < 0) + * return TC_ACT_SHOT; // drop packet + * + * if (key.remote_ipv4 != 0x0a000001) + * return TC_ACT_SHOT; // drop packet + * + * return TC_ACT_OK; // accept packet + * + * This interface can also be used with all encapsulation devices + * that can operate in "collect metadata" mode: instead of having + * one network device per specific configuration, the "collect + * metadata" mode only requires a single device where the + * configuration can be extracted from this helper. + * + * This can be used together with various tunnels such as VXLan, + * Geneve, GRE or IP in IP (IPIP). + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_get_tunnel_key)(struct __sk_buff *skb, struct bpf_tunnel_key *key, __u32 size, __u64 flags) = (void *) 20; + +/* + * bpf_skb_set_tunnel_key + * + * Populate tunnel metadata for packet associated to *skb.* The + * tunnel metadata is set to the contents of *key*, of *size*. The + * *flags* can be set to a combination of the following values: + * + * **BPF_F_TUNINFO_IPV6** + * Indicate that the tunnel is based on IPv6 protocol + * instead of IPv4. + * **BPF_F_ZERO_CSUM_TX** + * For IPv4 packets, add a flag to tunnel metadata + * indicating that checksum computation should be skipped + * and checksum set to zeroes. + * **BPF_F_DONT_FRAGMENT** + * Add a flag to tunnel metadata indicating that the + * packet should not be fragmented. + * **BPF_F_SEQ_NUMBER** + * Add a flag to tunnel metadata indicating that a + * sequence number should be added to tunnel header before + * sending the packet. This flag was added for GRE + * encapsulation, but might be used with other protocols + * as well in the future. + * + * Here is a typical usage on the transmit path: + * + * :: + * + * struct bpf_tunnel_key key; + * populate key ... + * bpf_skb_set_tunnel_key(skb, &key, sizeof(key), 0); + * bpf_clone_redirect(skb, vxlan_dev_ifindex, 0); + * + * See also the description of the **bpf_skb_get_tunnel_key**\ () + * helper for additional information. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_set_tunnel_key)(struct __sk_buff *skb, struct bpf_tunnel_key *key, __u32 size, __u64 flags) = (void *) 21; + +/* + * bpf_perf_event_read + * + * Read the value of a perf event counter. This helper relies on a + * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. The nature of + * the perf event counter is selected when *map* is updated with + * perf event file descriptors. The *map* is an array whose size + * is the number of available CPUs, and each cell contains a value + * relative to one CPU. The value to retrieve is indicated by + * *flags*, that contains the index of the CPU to look up, masked + * with **BPF_F_INDEX_MASK**. Alternatively, *flags* can be set to + * **BPF_F_CURRENT_CPU** to indicate that the value for the + * current CPU should be retrieved. + * + * Note that before Linux 4.13, only hardware perf event can be + * retrieved. + * + * Also, be aware that the newer helper + * **bpf_perf_event_read_value**\ () is recommended over + * **bpf_perf_event_read**\ () in general. The latter has some ABI + * quirks where error and counter value are used as a return code + * (which is wrong to do since ranges may overlap). This issue is + * fixed with **bpf_perf_event_read_value**\ (), which at the same + * time provides more features over the **bpf_perf_event_read**\ + * () interface. Please refer to the description of + * **bpf_perf_event_read_value**\ () for details. + * + * Returns + * The value of the perf event counter read from the map, or a + * negative error code in case of failure. + */ +static __u64 (*bpf_perf_event_read)(void *map, __u64 flags) = (void *) 22; + +/* + * bpf_redirect + * + * Redirect the packet to another net device of index *ifindex*. + * This helper is somewhat similar to **bpf_clone_redirect**\ + * (), except that the packet is not cloned, which provides + * increased performance. + * + * Except for XDP, both ingress and egress interfaces can be used + * for redirection. The **BPF_F_INGRESS** value in *flags* is used + * to make the distinction (ingress path is selected if the flag + * is present, egress path otherwise). Currently, XDP only + * supports redirection to the egress interface, and accepts no + * flag at all. + * + * The same effect can also be attained with the more generic + * **bpf_redirect_map**\ (), which uses a BPF map to store the + * redirect target instead of providing it directly to the helper. + * + * Returns + * For XDP, the helper returns **XDP_REDIRECT** on success or + * **XDP_ABORTED** on error. For other program types, the values + * are **TC_ACT_REDIRECT** on success or **TC_ACT_SHOT** on + * error. + */ +static long (*bpf_redirect)(__u32 ifindex, __u64 flags) = (void *) 23; + +/* + * bpf_get_route_realm + * + * Retrieve the realm or the route, that is to say the + * **tclassid** field of the destination for the *skb*. The + * identifier retrieved is a user-provided tag, similar to the + * one used with the net_cls cgroup (see description for + * **bpf_get_cgroup_classid**\ () helper), but here this tag is + * held by a route (a destination entry), not by a task. + * + * Retrieving this identifier works with the clsact TC egress hook + * (see also **tc-bpf(8)**), or alternatively on conventional + * classful egress qdiscs, but not on TC ingress path. In case of + * clsact TC egress hook, this has the advantage that, internally, + * the destination entry has not been dropped yet in the transmit + * path. Therefore, the destination entry does not need to be + * artificially held via **netif_keep_dst**\ () for a classful + * qdisc until the *skb* is freed. + * + * This helper is available only if the kernel was compiled with + * **CONFIG_IP_ROUTE_CLASSID** configuration option. + * + * Returns + * The realm of the route for the packet associated to *skb*, or 0 + * if none was found. + */ +static __u32 (*bpf_get_route_realm)(struct __sk_buff *skb) = (void *) 24; + +/* + * bpf_perf_event_output + * + * Write raw *data* blob into a special BPF perf event held by + * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf + * event must have the following attributes: **PERF_SAMPLE_RAW** + * as **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and + * **PERF_COUNT_SW_BPF_OUTPUT** as **config**. + * + * The *flags* are used to indicate the index in *map* for which + * the value must be put, masked with **BPF_F_INDEX_MASK**. + * Alternatively, *flags* can be set to **BPF_F_CURRENT_CPU** + * to indicate that the index of the current CPU core should be + * used. + * + * The value to write, of *size*, is passed through eBPF stack and + * pointed by *data*. + * + * The context of the program *ctx* needs also be passed to the + * helper. + * + * On user space, a program willing to read the values needs to + * call **perf_event_open**\ () on the perf event (either for + * one or for all CPUs) and to store the file descriptor into the + * *map*. This must be done before the eBPF program can send data + * into it. An example is available in file + * *samples/bpf/trace_output_user.c* in the Linux kernel source + * tree (the eBPF program counterpart is in + * *samples/bpf/trace_output_kern.c*). + * + * **bpf_perf_event_output**\ () achieves better performance + * than **bpf_trace_printk**\ () for sharing data with user + * space, and is much better suitable for streaming data from eBPF + * programs. + * + * Note that this helper is not restricted to tracing use cases + * and can be used with programs attached to TC or XDP as well, + * where it allows for passing data to user space listeners. Data + * can be: + * + * * Only custom structs, + * * Only the packet payload, or + * * A combination of both. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_perf_event_output)(void *ctx, void *map, __u64 flags, void *data, __u64 size) = (void *) 25; + +/* + * bpf_skb_load_bytes + * + * This helper was provided as an easy way to load data from a + * packet. It can be used to load *len* bytes from *offset* from + * the packet associated to *skb*, into the buffer pointed by + * *to*. + * + * Since Linux 4.7, usage of this helper has mostly been replaced + * by "direct packet access", enabling packet data to be + * manipulated with *skb*\ **->data** and *skb*\ **->data_end** + * pointing respectively to the first byte of packet data and to + * the byte after the last byte of packet data. However, it + * remains useful if one wishes to read large quantities of data + * at once from a packet into the eBPF stack. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_load_bytes)(const void *skb, __u32 offset, void *to, __u32 len) = (void *) 26; + +/* + * bpf_get_stackid + * + * Walk a user or a kernel stack and return its id. To achieve + * this, the helper needs *ctx*, which is a pointer to the context + * on which the tracing program is executed, and a pointer to a + * *map* of type **BPF_MAP_TYPE_STACK_TRACE**. + * + * The last argument, *flags*, holds the number of stack frames to + * skip (from 0 to 255), masked with + * **BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set + * a combination of the following flags: + * + * **BPF_F_USER_STACK** + * Collect a user space stack instead of a kernel stack. + * **BPF_F_FAST_STACK_CMP** + * Compare stacks by hash only. + * **BPF_F_REUSE_STACKID** + * If two different stacks hash into the same *stackid*, + * discard the old one. + * + * The stack id retrieved is a 32 bit long integer handle which + * can be further combined with other data (including other stack + * ids) and used as a key into maps. This can be useful for + * generating a variety of graphs (such as flame graphs or off-cpu + * graphs). + * + * For walking a stack, this helper is an improvement over + * **bpf_probe_read**\ (), which can be used with unrolled loops + * but is not efficient and consumes a lot of eBPF instructions. + * Instead, **bpf_get_stackid**\ () can collect up to + * **PERF_MAX_STACK_DEPTH** both kernel and user frames. Note that + * this limit can be controlled with the **sysctl** program, and + * that it should be manually increased in order to profile long + * user stacks (such as stacks for Java programs). To do so, use: + * + * :: + * + * # sysctl kernel.perf_event_max_stack=<new value> + * + * Returns + * The positive or null stack id on success, or a negative error + * in case of failure. + */ +static long (*bpf_get_stackid)(void *ctx, void *map, __u64 flags) = (void *) 27; + +/* + * bpf_csum_diff + * + * Compute a checksum difference, from the raw buffer pointed by + * *from*, of length *from_size* (that must be a multiple of 4), + * towards the raw buffer pointed by *to*, of size *to_size* + * (same remark). An optional *seed* can be added to the value + * (this can be cascaded, the seed may come from a previous call + * to the helper). + * + * This is flexible enough to be used in several ways: + * + * * With *from_size* == 0, *to_size* > 0 and *seed* set to + * checksum, it can be used when pushing new data. + * * With *from_size* > 0, *to_size* == 0 and *seed* set to + * checksum, it can be used when removing data from a packet. + * * With *from_size* > 0, *to_size* > 0 and *seed* set to 0, it + * can be used to compute a diff. Note that *from_size* and + * *to_size* do not need to be equal. + * + * This helper can be used in combination with + * **bpf_l3_csum_replace**\ () and **bpf_l4_csum_replace**\ (), to + * which one can feed in the difference computed with + * **bpf_csum_diff**\ (). + * + * Returns + * The checksum result, or a negative error code in case of + * failure. + */ +static __s64 (*bpf_csum_diff)(__be32 *from, __u32 from_size, __be32 *to, __u32 to_size, __wsum seed) = (void *) 28; + +/* + * bpf_skb_get_tunnel_opt + * + * Retrieve tunnel options metadata for the packet associated to + * *skb*, and store the raw tunnel option data to the buffer *opt* + * of *size*. + * + * This helper can be used with encapsulation devices that can + * operate in "collect metadata" mode (please refer to the related + * note in the description of **bpf_skb_get_tunnel_key**\ () for + * more details). A particular example where this can be used is + * in combination with the Geneve encapsulation protocol, where it + * allows for pushing (with **bpf_skb_get_tunnel_opt**\ () helper) + * and retrieving arbitrary TLVs (Type-Length-Value headers) from + * the eBPF program. This allows for full customization of these + * headers. + * + * Returns + * The size of the option data retrieved. + */ +static long (*bpf_skb_get_tunnel_opt)(struct __sk_buff *skb, void *opt, __u32 size) = (void *) 29; + +/* + * bpf_skb_set_tunnel_opt + * + * Set tunnel options metadata for the packet associated to *skb* + * to the option data contained in the raw buffer *opt* of *size*. + * + * See also the description of the **bpf_skb_get_tunnel_opt**\ () + * helper for additional information. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_set_tunnel_opt)(struct __sk_buff *skb, void *opt, __u32 size) = (void *) 30; + +/* + * bpf_skb_change_proto + * + * Change the protocol of the *skb* to *proto*. Currently + * supported are transition from IPv4 to IPv6, and from IPv6 to + * IPv4. The helper takes care of the groundwork for the + * transition, including resizing the socket buffer. The eBPF + * program is expected to fill the new headers, if any, via + * **skb_store_bytes**\ () and to recompute the checksums with + * **bpf_l3_csum_replace**\ () and **bpf_l4_csum_replace**\ + * (). The main case for this helper is to perform NAT64 + * operations out of an eBPF program. + * + * Internally, the GSO type is marked as dodgy so that headers are + * checked and segments are recalculated by the GSO/GRO engine. + * The size for GSO target is adapted as well. + * + * All values for *flags* are reserved for future usage, and must + * be left at zero. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_change_proto)(struct __sk_buff *skb, __be16 proto, __u64 flags) = (void *) 31; + +/* + * bpf_skb_change_type + * + * Change the packet type for the packet associated to *skb*. This + * comes down to setting *skb*\ **->pkt_type** to *type*, except + * the eBPF program does not have a write access to *skb*\ + * **->pkt_type** beside this helper. Using a helper here allows + * for graceful handling of errors. + * + * The major use case is to change incoming *skb*s to + * **PACKET_HOST** in a programmatic way instead of having to + * recirculate via **redirect**\ (..., **BPF_F_INGRESS**), for + * example. + * + * Note that *type* only allows certain values. At this time, they + * are: + * + * **PACKET_HOST** + * Packet is for us. + * **PACKET_BROADCAST** + * Send packet to all. + * **PACKET_MULTICAST** + * Send packet to group. + * **PACKET_OTHERHOST** + * Send packet to someone else. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_change_type)(struct __sk_buff *skb, __u32 type) = (void *) 32; + +/* + * bpf_skb_under_cgroup + * + * Check whether *skb* is a descendant of the cgroup2 held by + * *map* of type **BPF_MAP_TYPE_CGROUP_ARRAY**, at *index*. + * + * Returns + * The return value depends on the result of the test, and can be: + * + * * 0, if the *skb* failed the cgroup2 descendant test. + * * 1, if the *skb* succeeded the cgroup2 descendant test. + * * A negative error code, if an error occurred. + */ +static long (*bpf_skb_under_cgroup)(struct __sk_buff *skb, void *map, __u32 index) = (void *) 33; + +/* + * bpf_get_hash_recalc + * + * Retrieve the hash of the packet, *skb*\ **->hash**. If it is + * not set, in particular if the hash was cleared due to mangling, + * recompute this hash. Later accesses to the hash can be done + * directly with *skb*\ **->hash**. + * + * Calling **bpf_set_hash_invalid**\ (), changing a packet + * prototype with **bpf_skb_change_proto**\ (), or calling + * **bpf_skb_store_bytes**\ () with the + * **BPF_F_INVALIDATE_HASH** are actions susceptible to clear + * the hash and to trigger a new computation for the next call to + * **bpf_get_hash_recalc**\ (). + * + * Returns + * The 32-bit hash. + */ +static __u32 (*bpf_get_hash_recalc)(struct __sk_buff *skb) = (void *) 34; + +/* + * bpf_get_current_task + * + * Get the current task. + * + * Returns + * A pointer to the current task struct. + */ +static __u64 (*bpf_get_current_task)(void) = (void *) 35; + +/* + * bpf_probe_write_user + * + * Attempt in a safe way to write *len* bytes from the buffer + * *src* to *dst* in memory. It only works for threads that are in + * user context, and *dst* must be a valid user space address. + * + * This helper should not be used to implement any kind of + * security mechanism because of TOC-TOU attacks, but rather to + * debug, divert, and manipulate execution of semi-cooperative + * processes. + * + * Keep in mind that this feature is meant for experiments, and it + * has a risk of crashing the system and running programs. + * Therefore, when an eBPF program using this helper is attached, + * a warning including PID and process name is printed to kernel + * logs. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_probe_write_user)(void *dst, const void *src, __u32 len) = (void *) 36; + +/* + * bpf_current_task_under_cgroup + * + * Check whether the probe is being run is the context of a given + * subset of the cgroup2 hierarchy. The cgroup2 to test is held by + * *map* of type **BPF_MAP_TYPE_CGROUP_ARRAY**, at *index*. + * + * Returns + * The return value depends on the result of the test, and can be: + * + * * 1, if current task belongs to the cgroup2. + * * 0, if current task does not belong to the cgroup2. + * * A negative error code, if an error occurred. + */ +static long (*bpf_current_task_under_cgroup)(void *map, __u32 index) = (void *) 37; + +/* + * bpf_skb_change_tail + * + * Resize (trim or grow) the packet associated to *skb* to the + * new *len*. The *flags* are reserved for future usage, and must + * be left at zero. + * + * The basic idea is that the helper performs the needed work to + * change the size of the packet, then the eBPF program rewrites + * the rest via helpers like **bpf_skb_store_bytes**\ (), + * **bpf_l3_csum_replace**\ (), **bpf_l3_csum_replace**\ () + * and others. This helper is a slow path utility intended for + * replies with control messages. And because it is targeted for + * slow path, the helper itself can afford to be slow: it + * implicitly linearizes, unclones and drops offloads from the + * *skb*. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_change_tail)(struct __sk_buff *skb, __u32 len, __u64 flags) = (void *) 38; + +/* + * bpf_skb_pull_data + * + * Pull in non-linear data in case the *skb* is non-linear and not + * all of *len* are part of the linear section. Make *len* bytes + * from *skb* readable and writable. If a zero value is passed for + * *len*, then all bytes in the linear part of *skb* will be made + * readable and writable. + * + * This helper is only needed for reading and writing with direct + * packet access. + * + * For direct packet access, testing that offsets to access + * are within packet boundaries (test on *skb*\ **->data_end**) is + * susceptible to fail if offsets are invalid, or if the requested + * data is in non-linear parts of the *skb*. On failure the + * program can just bail out, or in the case of a non-linear + * buffer, use a helper to make the data available. The + * **bpf_skb_load_bytes**\ () helper is a first solution to access + * the data. Another one consists in using **bpf_skb_pull_data** + * to pull in once the non-linear parts, then retesting and + * eventually access the data. + * + * At the same time, this also makes sure the *skb* is uncloned, + * which is a necessary condition for direct write. As this needs + * to be an invariant for the write part only, the verifier + * detects writes and adds a prologue that is calling + * **bpf_skb_pull_data()** to effectively unclone the *skb* from + * the very beginning in case it is indeed cloned. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_pull_data)(struct __sk_buff *skb, __u32 len) = (void *) 39; + +/* + * bpf_csum_update + * + * Add the checksum *csum* into *skb*\ **->csum** in case the + * driver has supplied a checksum for the entire packet into that + * field. Return an error otherwise. This helper is intended to be + * used in combination with **bpf_csum_diff**\ (), in particular + * when the checksum needs to be updated after data has been + * written into the packet through direct packet access. + * + * Returns + * The checksum on success, or a negative error code in case of + * failure. + */ +static __s64 (*bpf_csum_update)(struct __sk_buff *skb, __wsum csum) = (void *) 40; + +/* + * bpf_set_hash_invalid + * + * Invalidate the current *skb*\ **->hash**. It can be used after + * mangling on headers through direct packet access, in order to + * indicate that the hash is outdated and to trigger a + * recalculation the next time the kernel tries to access this + * hash or when the **bpf_get_hash_recalc**\ () helper is called. + * + * Returns + * void. + */ +static void (*bpf_set_hash_invalid)(struct __sk_buff *skb) = (void *) 41; + +/* + * bpf_get_numa_node_id + * + * Return the id of the current NUMA node. The primary use case + * for this helper is the selection of sockets for the local NUMA + * node, when the program is attached to sockets using the + * **SO_ATTACH_REUSEPORT_EBPF** option (see also **socket(7)**), + * but the helper is also available to other eBPF program types, + * similarly to **bpf_get_smp_processor_id**\ (). + * + * Returns + * The id of current NUMA node. + */ +static long (*bpf_get_numa_node_id)(void) = (void *) 42; + +/* + * bpf_skb_change_head + * + * Grows headroom of packet associated to *skb* and adjusts the + * offset of the MAC header accordingly, adding *len* bytes of + * space. It automatically extends and reallocates memory as + * required. + * + * This helper can be used on a layer 3 *skb* to push a MAC header + * for redirection into a layer 2 device. + * + * All values for *flags* are reserved for future usage, and must + * be left at zero. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_change_head)(struct __sk_buff *skb, __u32 len, __u64 flags) = (void *) 43; + +/* + * bpf_xdp_adjust_head + * + * Adjust (move) *xdp_md*\ **->data** by *delta* bytes. Note that + * it is possible to use a negative value for *delta*. This helper + * can be used to prepare the packet for pushing or popping + * headers. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_xdp_adjust_head)(struct xdp_md *xdp_md, int delta) = (void *) 44; + +/* + * bpf_probe_read_str + * + * Copy a NUL terminated string from an unsafe kernel address + * *unsafe_ptr* to *dst*. See **bpf_probe_read_kernel_str**\ () for + * more details. + * + * Generally, use **bpf_probe_read_user_str**\ () or + * **bpf_probe_read_kernel_str**\ () instead. + * + * Returns + * On success, the strictly positive length of the string, + * including the trailing NUL character. On error, a negative + * value. + */ +static long (*bpf_probe_read_str)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 45; + +/* + * bpf_get_socket_cookie + * + * If the **struct sk_buff** pointed by *skb* has a known socket, + * retrieve the cookie (generated by the kernel) of this socket. + * If no cookie has been set yet, generate a new cookie. Once + * generated, the socket cookie remains stable for the life of the + * socket. This helper can be useful for monitoring per socket + * networking traffic statistics as it provides a global socket + * identifier that can be assumed unique. + * + * Returns + * A 8-byte long unique number on success, or 0 if the socket + * field is missing inside *skb*. + */ +static __u64 (*bpf_get_socket_cookie)(void *ctx) = (void *) 46; + +/* + * bpf_get_socket_uid + * + * Get the owner UID of the socked associated to *skb*. + * + * Returns + * The owner UID of the socket associated to *skb*. If the socket + * is **NULL**, or if it is not a full socket (i.e. if it is a + * time-wait or a request socket instead), **overflowuid** value + * is returned (note that **overflowuid** might also be the actual + * UID value for the socket). + */ +static __u32 (*bpf_get_socket_uid)(struct __sk_buff *skb) = (void *) 47; + +/* + * bpf_set_hash + * + * Set the full hash for *skb* (set the field *skb*\ **->hash**) + * to value *hash*. + * + * Returns + * 0 + */ +static long (*bpf_set_hash)(struct __sk_buff *skb, __u32 hash) = (void *) 48; + +/* + * bpf_setsockopt + * + * Emulate a call to **setsockopt()** on the socket associated to + * *bpf_socket*, which must be a full socket. The *level* at + * which the option resides and the name *optname* of the option + * must be specified, see **setsockopt(2)** for more information. + * The option value of length *optlen* is pointed by *optval*. + * + * *bpf_socket* should be one of the following: + * + * * **struct bpf_sock_ops** for **BPF_PROG_TYPE_SOCK_OPS**. + * * **struct bpf_sock_addr** for **BPF_CGROUP_INET4_CONNECT** + * and **BPF_CGROUP_INET6_CONNECT**. + * + * This helper actually implements a subset of **setsockopt()**. + * It supports the following *level*\ s: + * + * * **SOL_SOCKET**, which supports the following *optname*\ s: + * **SO_RCVBUF**, **SO_SNDBUF**, **SO_MAX_PACING_RATE**, + * **SO_PRIORITY**, **SO_RCVLOWAT**, **SO_MARK**, + * **SO_BINDTODEVICE**, **SO_KEEPALIVE**. + * * **IPPROTO_TCP**, which supports the following *optname*\ s: + * **TCP_CONGESTION**, **TCP_BPF_IW**, + * **TCP_BPF_SNDCWND_CLAMP**, **TCP_SAVE_SYN**, + * **TCP_KEEPIDLE**, **TCP_KEEPINTVL**, **TCP_KEEPCNT**, + * **TCP_SYNCNT**, **TCP_USER_TIMEOUT**, **TCP_NOTSENT_LOWAT**. + * * **IPPROTO_IP**, which supports *optname* **IP_TOS**. + * * **IPPROTO_IPV6**, which supports *optname* **IPV6_TCLASS**. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_setsockopt)(void *bpf_socket, int level, int optname, void *optval, int optlen) = (void *) 49; + +/* + * bpf_skb_adjust_room + * + * Grow or shrink the room for data in the packet associated to + * *skb* by *len_diff*, and according to the selected *mode*. + * + * By default, the helper will reset any offloaded checksum + * indicator of the skb to CHECKSUM_NONE. This can be avoided + * by the following flag: + * + * * **BPF_F_ADJ_ROOM_NO_CSUM_RESET**: Do not reset offloaded + * checksum data of the skb to CHECKSUM_NONE. + * + * There are two supported modes at this time: + * + * * **BPF_ADJ_ROOM_MAC**: Adjust room at the mac layer + * (room space is added or removed below the layer 2 header). + * + * * **BPF_ADJ_ROOM_NET**: Adjust room at the network layer + * (room space is added or removed below the layer 3 header). + * + * The following flags are supported at this time: + * + * * **BPF_F_ADJ_ROOM_FIXED_GSO**: Do not adjust gso_size. + * Adjusting mss in this way is not allowed for datagrams. + * + * * **BPF_F_ADJ_ROOM_ENCAP_L3_IPV4**, + * **BPF_F_ADJ_ROOM_ENCAP_L3_IPV6**: + * Any new space is reserved to hold a tunnel header. + * Configure skb offsets and other fields accordingly. + * + * * **BPF_F_ADJ_ROOM_ENCAP_L4_GRE**, + * **BPF_F_ADJ_ROOM_ENCAP_L4_UDP**: + * Use with ENCAP_L3 flags to further specify the tunnel type. + * + * * **BPF_F_ADJ_ROOM_ENCAP_L2**\ (*len*): + * Use with ENCAP_L3/L4 flags to further specify the tunnel + * type; *len* is the length of the inner MAC header. + * + * * **BPF_F_ADJ_ROOM_ENCAP_L2_ETH**: + * Use with BPF_F_ADJ_ROOM_ENCAP_L2 flag to further specify the + * L2 type as Ethernet. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_adjust_room)(struct __sk_buff *skb, __s32 len_diff, __u32 mode, __u64 flags) = (void *) 50; + +/* + * bpf_redirect_map + * + * Redirect the packet to the endpoint referenced by *map* at + * index *key*. Depending on its type, this *map* can contain + * references to net devices (for forwarding packets through other + * ports), or to CPUs (for redirecting XDP frames to another CPU; + * but this is only implemented for native XDP (with driver + * support) as of this writing). + * + * The lower two bits of *flags* are used as the return code if + * the map lookup fails. This is so that the return value can be + * one of the XDP program return codes up to **XDP_TX**, as chosen + * by the caller. The higher bits of *flags* can be set to + * BPF_F_BROADCAST or BPF_F_EXCLUDE_INGRESS as defined below. + * + * With BPF_F_BROADCAST the packet will be broadcasted to all the + * interfaces in the map, with BPF_F_EXCLUDE_INGRESS the ingress + * interface will be excluded when do broadcasting. + * + * See also **bpf_redirect**\ (), which only supports redirecting + * to an ifindex, but doesn't require a map to do so. + * + * Returns + * **XDP_REDIRECT** on success, or the value of the two lower bits + * of the *flags* argument on error. + */ +static long (*bpf_redirect_map)(void *map, __u32 key, __u64 flags) = (void *) 51; + +/* + * bpf_sk_redirect_map + * + * Redirect the packet to the socket referenced by *map* (of type + * **BPF_MAP_TYPE_SOCKMAP**) at index *key*. Both ingress and + * egress interfaces can be used for redirection. The + * **BPF_F_INGRESS** value in *flags* is used to make the + * distinction (ingress path is selected if the flag is present, + * egress path otherwise). This is the only flag supported for now. + * + * Returns + * **SK_PASS** on success, or **SK_DROP** on error. + */ +static long (*bpf_sk_redirect_map)(struct __sk_buff *skb, void *map, __u32 key, __u64 flags) = (void *) 52; + +/* + * bpf_sock_map_update + * + * Add an entry to, or update a *map* referencing sockets. The + * *skops* is used as a new value for the entry associated to + * *key*. *flags* is one of: + * + * **BPF_NOEXIST** + * The entry for *key* must not exist in the map. + * **BPF_EXIST** + * The entry for *key* must already exist in the map. + * **BPF_ANY** + * No condition on the existence of the entry for *key*. + * + * If the *map* has eBPF programs (parser and verdict), those will + * be inherited by the socket being added. If the socket is + * already attached to eBPF programs, this results in an error. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_sock_map_update)(struct bpf_sock_ops *skops, void *map, void *key, __u64 flags) = (void *) 53; + +/* + * bpf_xdp_adjust_meta + * + * Adjust the address pointed by *xdp_md*\ **->data_meta** by + * *delta* (which can be positive or negative). Note that this + * operation modifies the address stored in *xdp_md*\ **->data**, + * so the latter must be loaded only after the helper has been + * called. + * + * The use of *xdp_md*\ **->data_meta** is optional and programs + * are not required to use it. The rationale is that when the + * packet is processed with XDP (e.g. as DoS filter), it is + * possible to push further meta data along with it before passing + * to the stack, and to give the guarantee that an ingress eBPF + * program attached as a TC classifier on the same device can pick + * this up for further post-processing. Since TC works with socket + * buffers, it remains possible to set from XDP the **mark** or + * **priority** pointers, or other pointers for the socket buffer. + * Having this scratch space generic and programmable allows for + * more flexibility as the user is free to store whatever meta + * data they need. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_xdp_adjust_meta)(struct xdp_md *xdp_md, int delta) = (void *) 54; + +/* + * bpf_perf_event_read_value + * + * Read the value of a perf event counter, and store it into *buf* + * of size *buf_size*. This helper relies on a *map* of type + * **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. The nature of the perf event + * counter is selected when *map* is updated with perf event file + * descriptors. The *map* is an array whose size is the number of + * available CPUs, and each cell contains a value relative to one + * CPU. The value to retrieve is indicated by *flags*, that + * contains the index of the CPU to look up, masked with + * **BPF_F_INDEX_MASK**. Alternatively, *flags* can be set to + * **BPF_F_CURRENT_CPU** to indicate that the value for the + * current CPU should be retrieved. + * + * This helper behaves in a way close to + * **bpf_perf_event_read**\ () helper, save that instead of + * just returning the value observed, it fills the *buf* + * structure. This allows for additional data to be retrieved: in + * particular, the enabled and running times (in *buf*\ + * **->enabled** and *buf*\ **->running**, respectively) are + * copied. In general, **bpf_perf_event_read_value**\ () is + * recommended over **bpf_perf_event_read**\ (), which has some + * ABI issues and provides fewer functionalities. + * + * These values are interesting, because hardware PMU (Performance + * Monitoring Unit) counters are limited resources. When there are + * more PMU based perf events opened than available counters, + * kernel will multiplex these events so each event gets certain + * percentage (but not all) of the PMU time. In case that + * multiplexing happens, the number of samples or counter value + * will not reflect the case compared to when no multiplexing + * occurs. This makes comparison between different runs difficult. + * Typically, the counter value should be normalized before + * comparing to other experiments. The usual normalization is done + * as follows. + * + * :: + * + * normalized_counter = counter * t_enabled / t_running + * + * Where t_enabled is the time enabled for event and t_running is + * the time running for event since last normalization. The + * enabled and running times are accumulated since the perf event + * open. To achieve scaling factor between two invocations of an + * eBPF program, users can use CPU id as the key (which is + * typical for perf array usage model) to remember the previous + * value and do the calculation inside the eBPF program. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_perf_event_read_value)(void *map, __u64 flags, struct bpf_perf_event_value *buf, __u32 buf_size) = (void *) 55; + +/* + * bpf_perf_prog_read_value + * + * For en eBPF program attached to a perf event, retrieve the + * value of the event counter associated to *ctx* and store it in + * the structure pointed by *buf* and of size *buf_size*. Enabled + * and running times are also stored in the structure (see + * description of helper **bpf_perf_event_read_value**\ () for + * more details). + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_perf_prog_read_value)(struct bpf_perf_event_data *ctx, struct bpf_perf_event_value *buf, __u32 buf_size) = (void *) 56; + +/* + * bpf_getsockopt + * + * Emulate a call to **getsockopt()** on the socket associated to + * *bpf_socket*, which must be a full socket. The *level* at + * which the option resides and the name *optname* of the option + * must be specified, see **getsockopt(2)** for more information. + * The retrieved value is stored in the structure pointed by + * *opval* and of length *optlen*. + * + * *bpf_socket* should be one of the following: + * + * * **struct bpf_sock_ops** for **BPF_PROG_TYPE_SOCK_OPS**. + * * **struct bpf_sock_addr** for **BPF_CGROUP_INET4_CONNECT** + * and **BPF_CGROUP_INET6_CONNECT**. + * + * This helper actually implements a subset of **getsockopt()**. + * It supports the following *level*\ s: + * + * * **IPPROTO_TCP**, which supports *optname* + * **TCP_CONGESTION**. + * * **IPPROTO_IP**, which supports *optname* **IP_TOS**. + * * **IPPROTO_IPV6**, which supports *optname* **IPV6_TCLASS**. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_getsockopt)(void *bpf_socket, int level, int optname, void *optval, int optlen) = (void *) 57; + +/* + * bpf_override_return + * + * Used for error injection, this helper uses kprobes to override + * the return value of the probed function, and to set it to *rc*. + * The first argument is the context *regs* on which the kprobe + * works. + * + * This helper works by setting the PC (program counter) + * to an override function which is run in place of the original + * probed function. This means the probed function is not run at + * all. The replacement function just returns with the required + * value. + * + * This helper has security implications, and thus is subject to + * restrictions. It is only available if the kernel was compiled + * with the **CONFIG_BPF_KPROBE_OVERRIDE** configuration + * option, and in this case it only works on functions tagged with + * **ALLOW_ERROR_INJECTION** in the kernel code. + * + * Also, the helper is only available for the architectures having + * the CONFIG_FUNCTION_ERROR_INJECTION option. As of this writing, + * x86 architecture is the only one to support this feature. + * + * Returns + * 0 + */ +static long (*bpf_override_return)(struct pt_regs *regs, __u64 rc) = (void *) 58; + +/* + * bpf_sock_ops_cb_flags_set + * + * Attempt to set the value of the **bpf_sock_ops_cb_flags** field + * for the full TCP socket associated to *bpf_sock_ops* to + * *argval*. + * + * The primary use of this field is to determine if there should + * be calls to eBPF programs of type + * **BPF_PROG_TYPE_SOCK_OPS** at various points in the TCP + * code. A program of the same type can change its value, per + * connection and as necessary, when the connection is + * established. This field is directly accessible for reading, but + * this helper must be used for updates in order to return an + * error if an eBPF program tries to set a callback that is not + * supported in the current kernel. + * + * *argval* is a flag array which can combine these flags: + * + * * **BPF_SOCK_OPS_RTO_CB_FLAG** (retransmission time out) + * * **BPF_SOCK_OPS_RETRANS_CB_FLAG** (retransmission) + * * **BPF_SOCK_OPS_STATE_CB_FLAG** (TCP state change) + * * **BPF_SOCK_OPS_RTT_CB_FLAG** (every RTT) + * + * Therefore, this function can be used to clear a callback flag by + * setting the appropriate bit to zero. e.g. to disable the RTO + * callback: + * + * **bpf_sock_ops_cb_flags_set(bpf_sock,** + * **bpf_sock->bpf_sock_ops_cb_flags & ~BPF_SOCK_OPS_RTO_CB_FLAG)** + * + * Here are some examples of where one could call such eBPF + * program: + * + * * When RTO fires. + * * When a packet is retransmitted. + * * When the connection terminates. + * * When a packet is sent. + * * When a packet is received. + * + * Returns + * Code **-EINVAL** if the socket is not a full TCP socket; + * otherwise, a positive number containing the bits that could not + * be set is returned (which comes down to 0 if all bits were set + * as required). + */ +static long (*bpf_sock_ops_cb_flags_set)(struct bpf_sock_ops *bpf_sock, int argval) = (void *) 59; + +/* + * bpf_msg_redirect_map + * + * This helper is used in programs implementing policies at the + * socket level. If the message *msg* is allowed to pass (i.e. if + * the verdict eBPF program returns **SK_PASS**), redirect it to + * the socket referenced by *map* (of type + * **BPF_MAP_TYPE_SOCKMAP**) at index *key*. Both ingress and + * egress interfaces can be used for redirection. The + * **BPF_F_INGRESS** value in *flags* is used to make the + * distinction (ingress path is selected if the flag is present, + * egress path otherwise). This is the only flag supported for now. + * + * Returns + * **SK_PASS** on success, or **SK_DROP** on error. + */ +static long (*bpf_msg_redirect_map)(struct sk_msg_md *msg, void *map, __u32 key, __u64 flags) = (void *) 60; + +/* + * bpf_msg_apply_bytes + * + * For socket policies, apply the verdict of the eBPF program to + * the next *bytes* (number of bytes) of message *msg*. + * + * For example, this helper can be used in the following cases: + * + * * A single **sendmsg**\ () or **sendfile**\ () system call + * contains multiple logical messages that the eBPF program is + * supposed to read and for which it should apply a verdict. + * * An eBPF program only cares to read the first *bytes* of a + * *msg*. If the message has a large payload, then setting up + * and calling the eBPF program repeatedly for all bytes, even + * though the verdict is already known, would create unnecessary + * overhead. + * + * When called from within an eBPF program, the helper sets a + * counter internal to the BPF infrastructure, that is used to + * apply the last verdict to the next *bytes*. If *bytes* is + * smaller than the current data being processed from a + * **sendmsg**\ () or **sendfile**\ () system call, the first + * *bytes* will be sent and the eBPF program will be re-run with + * the pointer for start of data pointing to byte number *bytes* + * **+ 1**. If *bytes* is larger than the current data being + * processed, then the eBPF verdict will be applied to multiple + * **sendmsg**\ () or **sendfile**\ () calls until *bytes* are + * consumed. + * + * Note that if a socket closes with the internal counter holding + * a non-zero value, this is not a problem because data is not + * being buffered for *bytes* and is sent as it is received. + * + * Returns + * 0 + */ +static long (*bpf_msg_apply_bytes)(struct sk_msg_md *msg, __u32 bytes) = (void *) 61; + +/* + * bpf_msg_cork_bytes + * + * For socket policies, prevent the execution of the verdict eBPF + * program for message *msg* until *bytes* (byte number) have been + * accumulated. + * + * This can be used when one needs a specific number of bytes + * before a verdict can be assigned, even if the data spans + * multiple **sendmsg**\ () or **sendfile**\ () calls. The extreme + * case would be a user calling **sendmsg**\ () repeatedly with + * 1-byte long message segments. Obviously, this is bad for + * performance, but it is still valid. If the eBPF program needs + * *bytes* bytes to validate a header, this helper can be used to + * prevent the eBPF program to be called again until *bytes* have + * been accumulated. + * + * Returns + * 0 + */ +static long (*bpf_msg_cork_bytes)(struct sk_msg_md *msg, __u32 bytes) = (void *) 62; + +/* + * bpf_msg_pull_data + * + * For socket policies, pull in non-linear data from user space + * for *msg* and set pointers *msg*\ **->data** and *msg*\ + * **->data_end** to *start* and *end* bytes offsets into *msg*, + * respectively. + * + * If a program of type **BPF_PROG_TYPE_SK_MSG** is run on a + * *msg* it can only parse data that the (**data**, **data_end**) + * pointers have already consumed. For **sendmsg**\ () hooks this + * is likely the first scatterlist element. But for calls relying + * on the **sendpage** handler (e.g. **sendfile**\ ()) this will + * be the range (**0**, **0**) because the data is shared with + * user space and by default the objective is to avoid allowing + * user space to modify data while (or after) eBPF verdict is + * being decided. This helper can be used to pull in data and to + * set the start and end pointer to given values. Data will be + * copied if necessary (i.e. if data was not linear and if start + * and end pointers do not point to the same chunk). + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * All values for *flags* are reserved for future usage, and must + * be left at zero. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_msg_pull_data)(struct sk_msg_md *msg, __u32 start, __u32 end, __u64 flags) = (void *) 63; + +/* + * bpf_bind + * + * Bind the socket associated to *ctx* to the address pointed by + * *addr*, of length *addr_len*. This allows for making outgoing + * connection from the desired IP address, which can be useful for + * example when all processes inside a cgroup should use one + * single IP address on a host that has multiple IP configured. + * + * This helper works for IPv4 and IPv6, TCP and UDP sockets. The + * domain (*addr*\ **->sa_family**) must be **AF_INET** (or + * **AF_INET6**). It's advised to pass zero port (**sin_port** + * or **sin6_port**) which triggers IP_BIND_ADDRESS_NO_PORT-like + * behavior and lets the kernel efficiently pick up an unused + * port as long as 4-tuple is unique. Passing non-zero port might + * lead to degraded performance. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_bind)(struct bpf_sock_addr *ctx, struct sockaddr *addr, int addr_len) = (void *) 64; + +/* + * bpf_xdp_adjust_tail + * + * Adjust (move) *xdp_md*\ **->data_end** by *delta* bytes. It is + * possible to both shrink and grow the packet tail. + * Shrink done via *delta* being a negative integer. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_xdp_adjust_tail)(struct xdp_md *xdp_md, int delta) = (void *) 65; + +/* + * bpf_skb_get_xfrm_state + * + * Retrieve the XFRM state (IP transform framework, see also + * **ip-xfrm(8)**) at *index* in XFRM "security path" for *skb*. + * + * The retrieved value is stored in the **struct bpf_xfrm_state** + * pointed by *xfrm_state* and of length *size*. + * + * All values for *flags* are reserved for future usage, and must + * be left at zero. + * + * This helper is available only if the kernel was compiled with + * **CONFIG_XFRM** configuration option. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_get_xfrm_state)(struct __sk_buff *skb, __u32 index, struct bpf_xfrm_state *xfrm_state, __u32 size, __u64 flags) = (void *) 66; + +/* + * bpf_get_stack + * + * Return a user or a kernel stack in bpf program provided buffer. + * To achieve this, the helper needs *ctx*, which is a pointer + * to the context on which the tracing program is executed. + * To store the stacktrace, the bpf program provides *buf* with + * a nonnegative *size*. + * + * The last argument, *flags*, holds the number of stack frames to + * skip (from 0 to 255), masked with + * **BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set + * the following flags: + * + * **BPF_F_USER_STACK** + * Collect a user space stack instead of a kernel stack. + * **BPF_F_USER_BUILD_ID** + * Collect buildid+offset instead of ips for user stack, + * only valid if **BPF_F_USER_STACK** is also specified. + * + * **bpf_get_stack**\ () can collect up to + * **PERF_MAX_STACK_DEPTH** both kernel and user frames, subject + * to sufficient large buffer size. Note that + * this limit can be controlled with the **sysctl** program, and + * that it should be manually increased in order to profile long + * user stacks (such as stacks for Java programs). To do so, use: + * + * :: + * + * # sysctl kernel.perf_event_max_stack=<new value> + * + * Returns + * The non-negative copied *buf* length equal to or less than + * *size* on success, or a negative error in case of failure. + */ +static long (*bpf_get_stack)(void *ctx, void *buf, __u32 size, __u64 flags) = (void *) 67; + +/* + * bpf_skb_load_bytes_relative + * + * This helper is similar to **bpf_skb_load_bytes**\ () in that + * it provides an easy way to load *len* bytes from *offset* + * from the packet associated to *skb*, into the buffer pointed + * by *to*. The difference to **bpf_skb_load_bytes**\ () is that + * a fifth argument *start_header* exists in order to select a + * base offset to start from. *start_header* can be one of: + * + * **BPF_HDR_START_MAC** + * Base offset to load data from is *skb*'s mac header. + * **BPF_HDR_START_NET** + * Base offset to load data from is *skb*'s network header. + * + * In general, "direct packet access" is the preferred method to + * access packet data, however, this helper is in particular useful + * in socket filters where *skb*\ **->data** does not always point + * to the start of the mac header and where "direct packet access" + * is not available. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_load_bytes_relative)(const void *skb, __u32 offset, void *to, __u32 len, __u32 start_header) = (void *) 68; + +/* + * bpf_fib_lookup + * + * Do FIB lookup in kernel tables using parameters in *params*. + * If lookup is successful and result shows packet is to be + * forwarded, the neighbor tables are searched for the nexthop. + * If successful (ie., FIB lookup shows forwarding and nexthop + * is resolved), the nexthop address is returned in ipv4_dst + * or ipv6_dst based on family, smac is set to mac address of + * egress device, dmac is set to nexthop mac address, rt_metric + * is set to metric from route (IPv4/IPv6 only), and ifindex + * is set to the device index of the nexthop from the FIB lookup. + * + * *plen* argument is the size of the passed in struct. + * *flags* argument can be a combination of one or more of the + * following values: + * + * **BPF_FIB_LOOKUP_DIRECT** + * Do a direct table lookup vs full lookup using FIB + * rules. + * **BPF_FIB_LOOKUP_OUTPUT** + * Perform lookup from an egress perspective (default is + * ingress). + * + * *ctx* is either **struct xdp_md** for XDP programs or + * **struct sk_buff** tc cls_act programs. + * + * Returns + * * < 0 if any input argument is invalid + * * 0 on success (packet is forwarded, nexthop neighbor exists) + * * > 0 one of **BPF_FIB_LKUP_RET_** codes explaining why the + * packet is not forwarded or needs assist from full stack + * + * If lookup fails with BPF_FIB_LKUP_RET_FRAG_NEEDED, then the MTU + * was exceeded and output params->mtu_result contains the MTU. + */ +static long (*bpf_fib_lookup)(void *ctx, struct bpf_fib_lookup *params, int plen, __u32 flags) = (void *) 69; + +/* + * bpf_sock_hash_update + * + * Add an entry to, or update a sockhash *map* referencing sockets. + * The *skops* is used as a new value for the entry associated to + * *key*. *flags* is one of: + * + * **BPF_NOEXIST** + * The entry for *key* must not exist in the map. + * **BPF_EXIST** + * The entry for *key* must already exist in the map. + * **BPF_ANY** + * No condition on the existence of the entry for *key*. + * + * If the *map* has eBPF programs (parser and verdict), those will + * be inherited by the socket being added. If the socket is + * already attached to eBPF programs, this results in an error. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_sock_hash_update)(struct bpf_sock_ops *skops, void *map, void *key, __u64 flags) = (void *) 70; + +/* + * bpf_msg_redirect_hash + * + * This helper is used in programs implementing policies at the + * socket level. If the message *msg* is allowed to pass (i.e. if + * the verdict eBPF program returns **SK_PASS**), redirect it to + * the socket referenced by *map* (of type + * **BPF_MAP_TYPE_SOCKHASH**) using hash *key*. Both ingress and + * egress interfaces can be used for redirection. The + * **BPF_F_INGRESS** value in *flags* is used to make the + * distinction (ingress path is selected if the flag is present, + * egress path otherwise). This is the only flag supported for now. + * + * Returns + * **SK_PASS** on success, or **SK_DROP** on error. + */ +static long (*bpf_msg_redirect_hash)(struct sk_msg_md *msg, void *map, void *key, __u64 flags) = (void *) 71; + +/* + * bpf_sk_redirect_hash + * + * This helper is used in programs implementing policies at the + * skb socket level. If the sk_buff *skb* is allowed to pass (i.e. + * if the verdict eBPF program returns **SK_PASS**), redirect it + * to the socket referenced by *map* (of type + * **BPF_MAP_TYPE_SOCKHASH**) using hash *key*. Both ingress and + * egress interfaces can be used for redirection. The + * **BPF_F_INGRESS** value in *flags* is used to make the + * distinction (ingress path is selected if the flag is present, + * egress otherwise). This is the only flag supported for now. + * + * Returns + * **SK_PASS** on success, or **SK_DROP** on error. + */ +static long (*bpf_sk_redirect_hash)(struct __sk_buff *skb, void *map, void *key, __u64 flags) = (void *) 72; + +/* + * bpf_lwt_push_encap + * + * Encapsulate the packet associated to *skb* within a Layer 3 + * protocol header. This header is provided in the buffer at + * address *hdr*, with *len* its size in bytes. *type* indicates + * the protocol of the header and can be one of: + * + * **BPF_LWT_ENCAP_SEG6** + * IPv6 encapsulation with Segment Routing Header + * (**struct ipv6_sr_hdr**). *hdr* only contains the SRH, + * the IPv6 header is computed by the kernel. + * **BPF_LWT_ENCAP_SEG6_INLINE** + * Only works if *skb* contains an IPv6 packet. Insert a + * Segment Routing Header (**struct ipv6_sr_hdr**) inside + * the IPv6 header. + * **BPF_LWT_ENCAP_IP** + * IP encapsulation (GRE/GUE/IPIP/etc). The outer header + * must be IPv4 or IPv6, followed by zero or more + * additional headers, up to **LWT_BPF_MAX_HEADROOM** + * total bytes in all prepended headers. Please note that + * if **skb_is_gso**\ (*skb*) is true, no more than two + * headers can be prepended, and the inner header, if + * present, should be either GRE or UDP/GUE. + * + * **BPF_LWT_ENCAP_SEG6**\ \* types can be called by BPF programs + * of type **BPF_PROG_TYPE_LWT_IN**; **BPF_LWT_ENCAP_IP** type can + * be called by bpf programs of types **BPF_PROG_TYPE_LWT_IN** and + * **BPF_PROG_TYPE_LWT_XMIT**. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_lwt_push_encap)(struct __sk_buff *skb, __u32 type, void *hdr, __u32 len) = (void *) 73; + +/* + * bpf_lwt_seg6_store_bytes + * + * Store *len* bytes from address *from* into the packet + * associated to *skb*, at *offset*. Only the flags, tag and TLVs + * inside the outermost IPv6 Segment Routing Header can be + * modified through this helper. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_lwt_seg6_store_bytes)(struct __sk_buff *skb, __u32 offset, const void *from, __u32 len) = (void *) 74; + +/* + * bpf_lwt_seg6_adjust_srh + * + * Adjust the size allocated to TLVs in the outermost IPv6 + * Segment Routing Header contained in the packet associated to + * *skb*, at position *offset* by *delta* bytes. Only offsets + * after the segments are accepted. *delta* can be as well + * positive (growing) as negative (shrinking). + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_lwt_seg6_adjust_srh)(struct __sk_buff *skb, __u32 offset, __s32 delta) = (void *) 75; + +/* + * bpf_lwt_seg6_action + * + * Apply an IPv6 Segment Routing action of type *action* to the + * packet associated to *skb*. Each action takes a parameter + * contained at address *param*, and of length *param_len* bytes. + * *action* can be one of: + * + * **SEG6_LOCAL_ACTION_END_X** + * End.X action: Endpoint with Layer-3 cross-connect. + * Type of *param*: **struct in6_addr**. + * **SEG6_LOCAL_ACTION_END_T** + * End.T action: Endpoint with specific IPv6 table lookup. + * Type of *param*: **int**. + * **SEG6_LOCAL_ACTION_END_B6** + * End.B6 action: Endpoint bound to an SRv6 policy. + * Type of *param*: **struct ipv6_sr_hdr**. + * **SEG6_LOCAL_ACTION_END_B6_ENCAP** + * End.B6.Encap action: Endpoint bound to an SRv6 + * encapsulation policy. + * Type of *param*: **struct ipv6_sr_hdr**. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_lwt_seg6_action)(struct __sk_buff *skb, __u32 action, void *param, __u32 param_len) = (void *) 76; + +/* + * bpf_rc_repeat + * + * This helper is used in programs implementing IR decoding, to + * report a successfully decoded repeat key message. This delays + * the generation of a key up event for previously generated + * key down event. + * + * Some IR protocols like NEC have a special IR message for + * repeating last button, for when a button is held down. + * + * The *ctx* should point to the lirc sample as passed into + * the program. + * + * This helper is only available is the kernel was compiled with + * the **CONFIG_BPF_LIRC_MODE2** configuration option set to + * "**y**". + * + * Returns + * 0 + */ +static long (*bpf_rc_repeat)(void *ctx) = (void *) 77; + +/* + * bpf_rc_keydown + * + * This helper is used in programs implementing IR decoding, to + * report a successfully decoded key press with *scancode*, + * *toggle* value in the given *protocol*. The scancode will be + * translated to a keycode using the rc keymap, and reported as + * an input key down event. After a period a key up event is + * generated. This period can be extended by calling either + * **bpf_rc_keydown**\ () again with the same values, or calling + * **bpf_rc_repeat**\ (). + * + * Some protocols include a toggle bit, in case the button was + * released and pressed again between consecutive scancodes. + * + * The *ctx* should point to the lirc sample as passed into + * the program. + * + * The *protocol* is the decoded protocol number (see + * **enum rc_proto** for some predefined values). + * + * This helper is only available is the kernel was compiled with + * the **CONFIG_BPF_LIRC_MODE2** configuration option set to + * "**y**". + * + * Returns + * 0 + */ +static long (*bpf_rc_keydown)(void *ctx, __u32 protocol, __u64 scancode, __u32 toggle) = (void *) 78; + +/* + * bpf_skb_cgroup_id + * + * Return the cgroup v2 id of the socket associated with the *skb*. + * This is roughly similar to the **bpf_get_cgroup_classid**\ () + * helper for cgroup v1 by providing a tag resp. identifier that + * can be matched on or used for map lookups e.g. to implement + * policy. The cgroup v2 id of a given path in the hierarchy is + * exposed in user space through the f_handle API in order to get + * to the same 64-bit id. + * + * This helper can be used on TC egress path, but not on ingress, + * and is available only if the kernel was compiled with the + * **CONFIG_SOCK_CGROUP_DATA** configuration option. + * + * Returns + * The id is returned or 0 in case the id could not be retrieved. + */ +static __u64 (*bpf_skb_cgroup_id)(struct __sk_buff *skb) = (void *) 79; + +/* + * bpf_get_current_cgroup_id + * + * Get the current cgroup id based on the cgroup within which + * the current task is running. + * + * Returns + * A 64-bit integer containing the current cgroup id based + * on the cgroup within which the current task is running. + */ +static __u64 (*bpf_get_current_cgroup_id)(void) = (void *) 80; + +/* + * bpf_get_local_storage + * + * Get the pointer to the local storage area. + * The type and the size of the local storage is defined + * by the *map* argument. + * The *flags* meaning is specific for each map type, + * and has to be 0 for cgroup local storage. + * + * Depending on the BPF program type, a local storage area + * can be shared between multiple instances of the BPF program, + * running simultaneously. + * + * A user should care about the synchronization by himself. + * For example, by using the **BPF_ATOMIC** instructions to alter + * the shared data. + * + * Returns + * A pointer to the local storage area. + */ +static void *(*bpf_get_local_storage)(void *map, __u64 flags) = (void *) 81; + +/* + * bpf_sk_select_reuseport + * + * Select a **SO_REUSEPORT** socket from a + * **BPF_MAP_TYPE_REUSEPORT_SOCKARRAY** *map*. + * It checks the selected socket is matching the incoming + * request in the socket buffer. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_sk_select_reuseport)(struct sk_reuseport_md *reuse, void *map, void *key, __u64 flags) = (void *) 82; + +/* + * bpf_skb_ancestor_cgroup_id + * + * Return id of cgroup v2 that is ancestor of cgroup associated + * with the *skb* at the *ancestor_level*. The root cgroup is at + * *ancestor_level* zero and each step down the hierarchy + * increments the level. If *ancestor_level* == level of cgroup + * associated with *skb*, then return value will be same as that + * of **bpf_skb_cgroup_id**\ (). + * + * The helper is useful to implement policies based on cgroups + * that are upper in hierarchy than immediate cgroup associated + * with *skb*. + * + * The format of returned id and helper limitations are same as in + * **bpf_skb_cgroup_id**\ (). + * + * Returns + * The id is returned or 0 in case the id could not be retrieved. + */ +static __u64 (*bpf_skb_ancestor_cgroup_id)(struct __sk_buff *skb, int ancestor_level) = (void *) 83; + +/* + * bpf_sk_lookup_tcp + * + * Look for TCP socket matching *tuple*, optionally in a child + * network namespace *netns*. The return value must be checked, + * and if non-**NULL**, released via **bpf_sk_release**\ (). + * + * The *ctx* should point to the context of the program, such as + * the skb or socket (depending on the hook in use). This is used + * to determine the base network namespace for the lookup. + * + * *tuple_size* must be one of: + * + * **sizeof**\ (*tuple*\ **->ipv4**) + * Look for an IPv4 socket. + * **sizeof**\ (*tuple*\ **->ipv6**) + * Look for an IPv6 socket. + * + * If the *netns* is a negative signed 32-bit integer, then the + * socket lookup table in the netns associated with the *ctx* + * will be used. For the TC hooks, this is the netns of the device + * in the skb. For socket hooks, this is the netns of the socket. + * If *netns* is any other signed 32-bit value greater than or + * equal to zero then it specifies the ID of the netns relative to + * the netns associated with the *ctx*. *netns* values beyond the + * range of 32-bit integers are reserved for future use. + * + * All values for *flags* are reserved for future usage, and must + * be left at zero. + * + * This helper is available only if the kernel was compiled with + * **CONFIG_NET** configuration option. + * + * Returns + * Pointer to **struct bpf_sock**, or **NULL** in case of failure. + * For sockets with reuseport option, the **struct bpf_sock** + * result is from *reuse*\ **->socks**\ [] using the hash of the + * tuple. + */ +static struct bpf_sock *(*bpf_sk_lookup_tcp)(void *ctx, struct bpf_sock_tuple *tuple, __u32 tuple_size, __u64 netns, __u64 flags) = (void *) 84; + +/* + * bpf_sk_lookup_udp + * + * Look for UDP socket matching *tuple*, optionally in a child + * network namespace *netns*. The return value must be checked, + * and if non-**NULL**, released via **bpf_sk_release**\ (). + * + * The *ctx* should point to the context of the program, such as + * the skb or socket (depending on the hook in use). This is used + * to determine the base network namespace for the lookup. + * + * *tuple_size* must be one of: + * + * **sizeof**\ (*tuple*\ **->ipv4**) + * Look for an IPv4 socket. + * **sizeof**\ (*tuple*\ **->ipv6**) + * Look for an IPv6 socket. + * + * If the *netns* is a negative signed 32-bit integer, then the + * socket lookup table in the netns associated with the *ctx* + * will be used. For the TC hooks, this is the netns of the device + * in the skb. For socket hooks, this is the netns of the socket. + * If *netns* is any other signed 32-bit value greater than or + * equal to zero then it specifies the ID of the netns relative to + * the netns associated with the *ctx*. *netns* values beyond the + * range of 32-bit integers are reserved for future use. + * + * All values for *flags* are reserved for future usage, and must + * be left at zero. + * + * This helper is available only if the kernel was compiled with + * **CONFIG_NET** configuration option. + * + * Returns + * Pointer to **struct bpf_sock**, or **NULL** in case of failure. + * For sockets with reuseport option, the **struct bpf_sock** + * result is from *reuse*\ **->socks**\ [] using the hash of the + * tuple. + */ +static struct bpf_sock *(*bpf_sk_lookup_udp)(void *ctx, struct bpf_sock_tuple *tuple, __u32 tuple_size, __u64 netns, __u64 flags) = (void *) 85; + +/* + * bpf_sk_release + * + * Release the reference held by *sock*. *sock* must be a + * non-**NULL** pointer that was returned from + * **bpf_sk_lookup_xxx**\ (). + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_sk_release)(void *sock) = (void *) 86; + +/* + * bpf_map_push_elem + * + * Push an element *value* in *map*. *flags* is one of: + * + * **BPF_EXIST** + * If the queue/stack is full, the oldest element is + * removed to make room for this. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_map_push_elem)(void *map, const void *value, __u64 flags) = (void *) 87; + +/* + * bpf_map_pop_elem + * + * Pop an element from *map*. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_map_pop_elem)(void *map, void *value) = (void *) 88; + +/* + * bpf_map_peek_elem + * + * Get an element from *map* without removing it. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_map_peek_elem)(void *map, void *value) = (void *) 89; + +/* + * bpf_msg_push_data + * + * For socket policies, insert *len* bytes into *msg* at offset + * *start*. + * + * If a program of type **BPF_PROG_TYPE_SK_MSG** is run on a + * *msg* it may want to insert metadata or options into the *msg*. + * This can later be read and used by any of the lower layer BPF + * hooks. + * + * This helper may fail if under memory pressure (a malloc + * fails) in these cases BPF programs will get an appropriate + * error and BPF programs will need to handle them. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_msg_push_data)(struct sk_msg_md *msg, __u32 start, __u32 len, __u64 flags) = (void *) 90; + +/* + * bpf_msg_pop_data + * + * Will remove *len* bytes from a *msg* starting at byte *start*. + * This may result in **ENOMEM** errors under certain situations if + * an allocation and copy are required due to a full ring buffer. + * However, the helper will try to avoid doing the allocation + * if possible. Other errors can occur if input parameters are + * invalid either due to *start* byte not being valid part of *msg* + * payload and/or *pop* value being to large. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_msg_pop_data)(struct sk_msg_md *msg, __u32 start, __u32 len, __u64 flags) = (void *) 91; + +/* + * bpf_rc_pointer_rel + * + * This helper is used in programs implementing IR decoding, to + * report a successfully decoded pointer movement. + * + * The *ctx* should point to the lirc sample as passed into + * the program. + * + * This helper is only available is the kernel was compiled with + * the **CONFIG_BPF_LIRC_MODE2** configuration option set to + * "**y**". + * + * Returns + * 0 + */ +static long (*bpf_rc_pointer_rel)(void *ctx, __s32 rel_x, __s32 rel_y) = (void *) 92; + +/* + * bpf_spin_lock + * + * Acquire a spinlock represented by the pointer *lock*, which is + * stored as part of a value of a map. Taking the lock allows to + * safely update the rest of the fields in that value. The + * spinlock can (and must) later be released with a call to + * **bpf_spin_unlock**\ (\ *lock*\ ). + * + * Spinlocks in BPF programs come with a number of restrictions + * and constraints: + * + * * **bpf_spin_lock** objects are only allowed inside maps of + * types **BPF_MAP_TYPE_HASH** and **BPF_MAP_TYPE_ARRAY** (this + * list could be extended in the future). + * * BTF description of the map is mandatory. + * * The BPF program can take ONE lock at a time, since taking two + * or more could cause dead locks. + * * Only one **struct bpf_spin_lock** is allowed per map element. + * * When the lock is taken, calls (either BPF to BPF or helpers) + * are not allowed. + * * The **BPF_LD_ABS** and **BPF_LD_IND** instructions are not + * allowed inside a spinlock-ed region. + * * The BPF program MUST call **bpf_spin_unlock**\ () to release + * the lock, on all execution paths, before it returns. + * * The BPF program can access **struct bpf_spin_lock** only via + * the **bpf_spin_lock**\ () and **bpf_spin_unlock**\ () + * helpers. Loading or storing data into the **struct + * bpf_spin_lock** *lock*\ **;** field of a map is not allowed. + * * To use the **bpf_spin_lock**\ () helper, the BTF description + * of the map value must be a struct and have **struct + * bpf_spin_lock** *anyname*\ **;** field at the top level. + * Nested lock inside another struct is not allowed. + * * The **struct bpf_spin_lock** *lock* field in a map value must + * be aligned on a multiple of 4 bytes in that value. + * * Syscall with command **BPF_MAP_LOOKUP_ELEM** does not copy + * the **bpf_spin_lock** field to user space. + * * Syscall with command **BPF_MAP_UPDATE_ELEM**, or update from + * a BPF program, do not update the **bpf_spin_lock** field. + * * **bpf_spin_lock** cannot be on the stack or inside a + * networking packet (it can only be inside of a map values). + * * **bpf_spin_lock** is available to root only. + * * Tracing programs and socket filter programs cannot use + * **bpf_spin_lock**\ () due to insufficient preemption checks + * (but this may change in the future). + * * **bpf_spin_lock** is not allowed in inner maps of map-in-map. + * + * Returns + * 0 + */ +static long (*bpf_spin_lock)(struct bpf_spin_lock *lock) = (void *) 93; + +/* + * bpf_spin_unlock + * + * Release the *lock* previously locked by a call to + * **bpf_spin_lock**\ (\ *lock*\ ). + * + * Returns + * 0 + */ +static long (*bpf_spin_unlock)(struct bpf_spin_lock *lock) = (void *) 94; + +/* + * bpf_sk_fullsock + * + * This helper gets a **struct bpf_sock** pointer such + * that all the fields in this **bpf_sock** can be accessed. + * + * Returns + * A **struct bpf_sock** pointer on success, or **NULL** in + * case of failure. + */ +static struct bpf_sock *(*bpf_sk_fullsock)(struct bpf_sock *sk) = (void *) 95; + +/* + * bpf_tcp_sock + * + * This helper gets a **struct bpf_tcp_sock** pointer from a + * **struct bpf_sock** pointer. + * + * Returns + * A **struct bpf_tcp_sock** pointer on success, or **NULL** in + * case of failure. + */ +static struct bpf_tcp_sock *(*bpf_tcp_sock)(struct bpf_sock *sk) = (void *) 96; + +/* + * bpf_skb_ecn_set_ce + * + * Set ECN (Explicit Congestion Notification) field of IP header + * to **CE** (Congestion Encountered) if current value is **ECT** + * (ECN Capable Transport). Otherwise, do nothing. Works with IPv6 + * and IPv4. + * + * Returns + * 1 if the **CE** flag is set (either by the current helper call + * or because it was already present), 0 if it is not set. + */ +static long (*bpf_skb_ecn_set_ce)(struct __sk_buff *skb) = (void *) 97; + +/* + * bpf_get_listener_sock + * + * Return a **struct bpf_sock** pointer in **TCP_LISTEN** state. + * **bpf_sk_release**\ () is unnecessary and not allowed. + * + * Returns + * A **struct bpf_sock** pointer on success, or **NULL** in + * case of failure. + */ +static struct bpf_sock *(*bpf_get_listener_sock)(struct bpf_sock *sk) = (void *) 98; + +/* + * bpf_skc_lookup_tcp + * + * Look for TCP socket matching *tuple*, optionally in a child + * network namespace *netns*. The return value must be checked, + * and if non-**NULL**, released via **bpf_sk_release**\ (). + * + * This function is identical to **bpf_sk_lookup_tcp**\ (), except + * that it also returns timewait or request sockets. Use + * **bpf_sk_fullsock**\ () or **bpf_tcp_sock**\ () to access the + * full structure. + * + * This helper is available only if the kernel was compiled with + * **CONFIG_NET** configuration option. + * + * Returns + * Pointer to **struct bpf_sock**, or **NULL** in case of failure. + * For sockets with reuseport option, the **struct bpf_sock** + * result is from *reuse*\ **->socks**\ [] using the hash of the + * tuple. + */ +static struct bpf_sock *(*bpf_skc_lookup_tcp)(void *ctx, struct bpf_sock_tuple *tuple, __u32 tuple_size, __u64 netns, __u64 flags) = (void *) 99; + +/* + * bpf_tcp_check_syncookie + * + * Check whether *iph* and *th* contain a valid SYN cookie ACK for + * the listening socket in *sk*. + * + * *iph* points to the start of the IPv4 or IPv6 header, while + * *iph_len* contains **sizeof**\ (**struct iphdr**) or + * **sizeof**\ (**struct ipv6hdr**). + * + * *th* points to the start of the TCP header, while *th_len* + * contains the length of the TCP header (at least + * **sizeof**\ (**struct tcphdr**)). + * + * Returns + * 0 if *iph* and *th* are a valid SYN cookie ACK, or a negative + * error otherwise. + */ +static long (*bpf_tcp_check_syncookie)(void *sk, void *iph, __u32 iph_len, struct tcphdr *th, __u32 th_len) = (void *) 100; + +/* + * bpf_sysctl_get_name + * + * Get name of sysctl in /proc/sys/ and copy it into provided by + * program buffer *buf* of size *buf_len*. + * + * The buffer is always NUL terminated, unless it's zero-sized. + * + * If *flags* is zero, full name (e.g. "net/ipv4/tcp_mem") is + * copied. Use **BPF_F_SYSCTL_BASE_NAME** flag to copy base name + * only (e.g. "tcp_mem"). + * + * Returns + * Number of character copied (not including the trailing NUL). + * + * **-E2BIG** if the buffer wasn't big enough (*buf* will contain + * truncated name in this case). + */ +static long (*bpf_sysctl_get_name)(struct bpf_sysctl *ctx, char *buf, unsigned long buf_len, __u64 flags) = (void *) 101; + +/* + * bpf_sysctl_get_current_value + * + * Get current value of sysctl as it is presented in /proc/sys + * (incl. newline, etc), and copy it as a string into provided + * by program buffer *buf* of size *buf_len*. + * + * The whole value is copied, no matter what file position user + * space issued e.g. sys_read at. + * + * The buffer is always NUL terminated, unless it's zero-sized. + * + * Returns + * Number of character copied (not including the trailing NUL). + * + * **-E2BIG** if the buffer wasn't big enough (*buf* will contain + * truncated name in this case). + * + * **-EINVAL** if current value was unavailable, e.g. because + * sysctl is uninitialized and read returns -EIO for it. + */ +static long (*bpf_sysctl_get_current_value)(struct bpf_sysctl *ctx, char *buf, unsigned long buf_len) = (void *) 102; + +/* + * bpf_sysctl_get_new_value + * + * Get new value being written by user space to sysctl (before + * the actual write happens) and copy it as a string into + * provided by program buffer *buf* of size *buf_len*. + * + * User space may write new value at file position > 0. + * + * The buffer is always NUL terminated, unless it's zero-sized. + * + * Returns + * Number of character copied (not including the trailing NUL). + * + * **-E2BIG** if the buffer wasn't big enough (*buf* will contain + * truncated name in this case). + * + * **-EINVAL** if sysctl is being read. + */ +static long (*bpf_sysctl_get_new_value)(struct bpf_sysctl *ctx, char *buf, unsigned long buf_len) = (void *) 103; + +/* + * bpf_sysctl_set_new_value + * + * Override new value being written by user space to sysctl with + * value provided by program in buffer *buf* of size *buf_len*. + * + * *buf* should contain a string in same form as provided by user + * space on sysctl write. + * + * User space may write new value at file position > 0. To override + * the whole sysctl value file position should be set to zero. + * + * Returns + * 0 on success. + * + * **-E2BIG** if the *buf_len* is too big. + * + * **-EINVAL** if sysctl is being read. + */ +static long (*bpf_sysctl_set_new_value)(struct bpf_sysctl *ctx, const char *buf, unsigned long buf_len) = (void *) 104; + +/* + * bpf_strtol + * + * Convert the initial part of the string from buffer *buf* of + * size *buf_len* to a long integer according to the given base + * and save the result in *res*. + * + * The string may begin with an arbitrary amount of white space + * (as determined by **isspace**\ (3)) followed by a single + * optional '**-**' sign. + * + * Five least significant bits of *flags* encode base, other bits + * are currently unused. + * + * Base must be either 8, 10, 16 or 0 to detect it automatically + * similar to user space **strtol**\ (3). + * + * Returns + * Number of characters consumed on success. Must be positive but + * no more than *buf_len*. + * + * **-EINVAL** if no valid digits were found or unsupported base + * was provided. + * + * **-ERANGE** if resulting value was out of range. + */ +static long (*bpf_strtol)(const char *buf, unsigned long buf_len, __u64 flags, long *res) = (void *) 105; + +/* + * bpf_strtoul + * + * Convert the initial part of the string from buffer *buf* of + * size *buf_len* to an unsigned long integer according to the + * given base and save the result in *res*. + * + * The string may begin with an arbitrary amount of white space + * (as determined by **isspace**\ (3)). + * + * Five least significant bits of *flags* encode base, other bits + * are currently unused. + * + * Base must be either 8, 10, 16 or 0 to detect it automatically + * similar to user space **strtoul**\ (3). + * + * Returns + * Number of characters consumed on success. Must be positive but + * no more than *buf_len*. + * + * **-EINVAL** if no valid digits were found or unsupported base + * was provided. + * + * **-ERANGE** if resulting value was out of range. + */ +static long (*bpf_strtoul)(const char *buf, unsigned long buf_len, __u64 flags, unsigned long *res) = (void *) 106; + +/* + * bpf_sk_storage_get + * + * Get a bpf-local-storage from a *sk*. + * + * Logically, it could be thought of getting the value from + * a *map* with *sk* as the **key**. From this + * perspective, the usage is not much different from + * **bpf_map_lookup_elem**\ (*map*, **&**\ *sk*) except this + * helper enforces the key must be a full socket and the map must + * be a **BPF_MAP_TYPE_SK_STORAGE** also. + * + * Underneath, the value is stored locally at *sk* instead of + * the *map*. The *map* is used as the bpf-local-storage + * "type". The bpf-local-storage "type" (i.e. the *map*) is + * searched against all bpf-local-storages residing at *sk*. + * + * *sk* is a kernel **struct sock** pointer for LSM program. + * *sk* is a **struct bpf_sock** pointer for other program types. + * + * An optional *flags* (**BPF_SK_STORAGE_GET_F_CREATE**) can be + * used such that a new bpf-local-storage will be + * created if one does not exist. *value* can be used + * together with **BPF_SK_STORAGE_GET_F_CREATE** to specify + * the initial value of a bpf-local-storage. If *value* is + * **NULL**, the new bpf-local-storage will be zero initialized. + * + * Returns + * A bpf-local-storage pointer is returned on success. + * + * **NULL** if not found or there was an error in adding + * a new bpf-local-storage. + */ +static void *(*bpf_sk_storage_get)(void *map, void *sk, void *value, __u64 flags) = (void *) 107; + +/* + * bpf_sk_storage_delete + * + * Delete a bpf-local-storage from a *sk*. + * + * Returns + * 0 on success. + * + * **-ENOENT** if the bpf-local-storage cannot be found. + * **-EINVAL** if sk is not a fullsock (e.g. a request_sock). + */ +static long (*bpf_sk_storage_delete)(void *map, void *sk) = (void *) 108; + +/* + * bpf_send_signal + * + * Send signal *sig* to the process of the current task. + * The signal may be delivered to any of this process's threads. + * + * Returns + * 0 on success or successfully queued. + * + * **-EBUSY** if work queue under nmi is full. + * + * **-EINVAL** if *sig* is invalid. + * + * **-EPERM** if no permission to send the *sig*. + * + * **-EAGAIN** if bpf program can try again. + */ +static long (*bpf_send_signal)(__u32 sig) = (void *) 109; + +/* + * bpf_tcp_gen_syncookie + * + * Try to issue a SYN cookie for the packet with corresponding + * IP/TCP headers, *iph* and *th*, on the listening socket in *sk*. + * + * *iph* points to the start of the IPv4 or IPv6 header, while + * *iph_len* contains **sizeof**\ (**struct iphdr**) or + * **sizeof**\ (**struct ipv6hdr**). + * + * *th* points to the start of the TCP header, while *th_len* + * contains the length of the TCP header with options (at least + * **sizeof**\ (**struct tcphdr**)). + * + * Returns + * On success, lower 32 bits hold the generated SYN cookie in + * followed by 16 bits which hold the MSS value for that cookie, + * and the top 16 bits are unused. + * + * On failure, the returned value is one of the following: + * + * **-EINVAL** SYN cookie cannot be issued due to error + * + * **-ENOENT** SYN cookie should not be issued (no SYN flood) + * + * **-EOPNOTSUPP** kernel configuration does not enable SYN cookies + * + * **-EPROTONOSUPPORT** IP packet version is not 4 or 6 + */ +static __s64 (*bpf_tcp_gen_syncookie)(void *sk, void *iph, __u32 iph_len, struct tcphdr *th, __u32 th_len) = (void *) 110; + +/* + * bpf_skb_output + * + * Write raw *data* blob into a special BPF perf event held by + * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf + * event must have the following attributes: **PERF_SAMPLE_RAW** + * as **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and + * **PERF_COUNT_SW_BPF_OUTPUT** as **config**. + * + * The *flags* are used to indicate the index in *map* for which + * the value must be put, masked with **BPF_F_INDEX_MASK**. + * Alternatively, *flags* can be set to **BPF_F_CURRENT_CPU** + * to indicate that the index of the current CPU core should be + * used. + * + * The value to write, of *size*, is passed through eBPF stack and + * pointed by *data*. + * + * *ctx* is a pointer to in-kernel struct sk_buff. + * + * This helper is similar to **bpf_perf_event_output**\ () but + * restricted to raw_tracepoint bpf programs. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_output)(void *ctx, void *map, __u64 flags, void *data, __u64 size) = (void *) 111; + +/* + * bpf_probe_read_user + * + * Safely attempt to read *size* bytes from user space address + * *unsafe_ptr* and store the data in *dst*. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_probe_read_user)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 112; + +/* + * bpf_probe_read_kernel + * + * Safely attempt to read *size* bytes from kernel space address + * *unsafe_ptr* and store the data in *dst*. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_probe_read_kernel)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 113; + +/* + * bpf_probe_read_user_str + * + * Copy a NUL terminated string from an unsafe user address + * *unsafe_ptr* to *dst*. The *size* should include the + * terminating NUL byte. In case the string length is smaller than + * *size*, the target is not padded with further NUL bytes. If the + * string length is larger than *size*, just *size*-1 bytes are + * copied and the last byte is set to NUL. + * + * On success, returns the number of bytes that were written, + * including the terminal NUL. This makes this helper useful in + * tracing programs for reading strings, and more importantly to + * get its length at runtime. See the following snippet: + * + * :: + * + * SEC("kprobe/sys_open") + * void bpf_sys_open(struct pt_regs *ctx) + * { + * char buf[PATHLEN]; // PATHLEN is defined to 256 + * int res = bpf_probe_read_user_str(buf, sizeof(buf), + * ctx->di); + * + * // Consume buf, for example push it to + * // userspace via bpf_perf_event_output(); we + * // can use res (the string length) as event + * // size, after checking its boundaries. + * } + * + * In comparison, using **bpf_probe_read_user**\ () helper here + * instead to read the string would require to estimate the length + * at compile time, and would often result in copying more memory + * than necessary. + * + * Another useful use case is when parsing individual process + * arguments or individual environment variables navigating + * *current*\ **->mm->arg_start** and *current*\ + * **->mm->env_start**: using this helper and the return value, + * one can quickly iterate at the right offset of the memory area. + * + * Returns + * On success, the strictly positive length of the output string, + * including the trailing NUL character. On error, a negative + * value. + */ +static long (*bpf_probe_read_user_str)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 114; + +/* + * bpf_probe_read_kernel_str + * + * Copy a NUL terminated string from an unsafe kernel address *unsafe_ptr* + * to *dst*. Same semantics as with **bpf_probe_read_user_str**\ () apply. + * + * Returns + * On success, the strictly positive length of the string, including + * the trailing NUL character. On error, a negative value. + */ +static long (*bpf_probe_read_kernel_str)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 115; + +/* + * bpf_tcp_send_ack + * + * Send out a tcp-ack. *tp* is the in-kernel struct **tcp_sock**. + * *rcv_nxt* is the ack_seq to be sent out. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_tcp_send_ack)(void *tp, __u32 rcv_nxt) = (void *) 116; + +/* + * bpf_send_signal_thread + * + * Send signal *sig* to the thread corresponding to the current task. + * + * Returns + * 0 on success or successfully queued. + * + * **-EBUSY** if work queue under nmi is full. + * + * **-EINVAL** if *sig* is invalid. + * + * **-EPERM** if no permission to send the *sig*. + * + * **-EAGAIN** if bpf program can try again. + */ +static long (*bpf_send_signal_thread)(__u32 sig) = (void *) 117; + +/* + * bpf_jiffies64 + * + * Obtain the 64bit jiffies + * + * Returns + * The 64 bit jiffies + */ +static __u64 (*bpf_jiffies64)(void) = (void *) 118; + +/* + * bpf_read_branch_records + * + * For an eBPF program attached to a perf event, retrieve the + * branch records (**struct perf_branch_entry**) associated to *ctx* + * and store it in the buffer pointed by *buf* up to size + * *size* bytes. + * + * Returns + * On success, number of bytes written to *buf*. On error, a + * negative value. + * + * The *flags* can be set to **BPF_F_GET_BRANCH_RECORDS_SIZE** to + * instead return the number of bytes required to store all the + * branch entries. If this flag is set, *buf* may be NULL. + * + * **-EINVAL** if arguments invalid or **size** not a multiple + * of **sizeof**\ (**struct perf_branch_entry**\ ). + * + * **-ENOENT** if architecture does not support branch records. + */ +static long (*bpf_read_branch_records)(struct bpf_perf_event_data *ctx, void *buf, __u32 size, __u64 flags) = (void *) 119; + +/* + * bpf_get_ns_current_pid_tgid + * + * Returns 0 on success, values for *pid* and *tgid* as seen from the current + * *namespace* will be returned in *nsdata*. + * + * Returns + * 0 on success, or one of the following in case of failure: + * + * **-EINVAL** if dev and inum supplied don't match dev_t and inode number + * with nsfs of current task, or if dev conversion to dev_t lost high bits. + * + * **-ENOENT** if pidns does not exists for the current task. + */ +static long (*bpf_get_ns_current_pid_tgid)(__u64 dev, __u64 ino, struct bpf_pidns_info *nsdata, __u32 size) = (void *) 120; + +/* + * bpf_xdp_output + * + * Write raw *data* blob into a special BPF perf event held by + * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf + * event must have the following attributes: **PERF_SAMPLE_RAW** + * as **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and + * **PERF_COUNT_SW_BPF_OUTPUT** as **config**. + * + * The *flags* are used to indicate the index in *map* for which + * the value must be put, masked with **BPF_F_INDEX_MASK**. + * Alternatively, *flags* can be set to **BPF_F_CURRENT_CPU** + * to indicate that the index of the current CPU core should be + * used. + * + * The value to write, of *size*, is passed through eBPF stack and + * pointed by *data*. + * + * *ctx* is a pointer to in-kernel struct xdp_buff. + * + * This helper is similar to **bpf_perf_eventoutput**\ () but + * restricted to raw_tracepoint bpf programs. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_xdp_output)(void *ctx, void *map, __u64 flags, void *data, __u64 size) = (void *) 121; + +/* + * bpf_get_netns_cookie + * + * Retrieve the cookie (generated by the kernel) of the network + * namespace the input *ctx* is associated with. The network + * namespace cookie remains stable for its lifetime and provides + * a global identifier that can be assumed unique. If *ctx* is + * NULL, then the helper returns the cookie for the initial + * network namespace. The cookie itself is very similar to that + * of **bpf_get_socket_cookie**\ () helper, but for network + * namespaces instead of sockets. + * + * Returns + * A 8-byte long opaque number. + */ +static __u64 (*bpf_get_netns_cookie)(void *ctx) = (void *) 122; + +/* + * bpf_get_current_ancestor_cgroup_id + * + * Return id of cgroup v2 that is ancestor of the cgroup associated + * with the current task at the *ancestor_level*. The root cgroup + * is at *ancestor_level* zero and each step down the hierarchy + * increments the level. If *ancestor_level* == level of cgroup + * associated with the current task, then return value will be the + * same as that of **bpf_get_current_cgroup_id**\ (). + * + * The helper is useful to implement policies based on cgroups + * that are upper in hierarchy than immediate cgroup associated + * with the current task. + * + * The format of returned id and helper limitations are same as in + * **bpf_get_current_cgroup_id**\ (). + * + * Returns + * The id is returned or 0 in case the id could not be retrieved. + */ +static __u64 (*bpf_get_current_ancestor_cgroup_id)(int ancestor_level) = (void *) 123; + +/* + * bpf_sk_assign + * + * Helper is overloaded depending on BPF program type. This + * description applies to **BPF_PROG_TYPE_SCHED_CLS** and + * **BPF_PROG_TYPE_SCHED_ACT** programs. + * + * Assign the *sk* to the *skb*. When combined with appropriate + * routing configuration to receive the packet towards the socket, + * will cause *skb* to be delivered to the specified socket. + * Subsequent redirection of *skb* via **bpf_redirect**\ (), + * **bpf_clone_redirect**\ () or other methods outside of BPF may + * interfere with successful delivery to the socket. + * + * This operation is only valid from TC ingress path. + * + * The *flags* argument must be zero. + * + * Returns + * 0 on success, or a negative error in case of failure: + * + * **-EINVAL** if specified *flags* are not supported. + * + * **-ENOENT** if the socket is unavailable for assignment. + * + * **-ENETUNREACH** if the socket is unreachable (wrong netns). + * + * **-EOPNOTSUPP** if the operation is not supported, for example + * a call from outside of TC ingress. + * + * **-ESOCKTNOSUPPORT** if the socket type is not supported + * (reuseport). + */ +static long (*bpf_sk_assign)(void *ctx, void *sk, __u64 flags) = (void *) 124; + +/* + * bpf_ktime_get_boot_ns + * + * Return the time elapsed since system boot, in nanoseconds. + * Does include the time the system was suspended. + * See: **clock_gettime**\ (**CLOCK_BOOTTIME**) + * + * Returns + * Current *ktime*. + */ +static __u64 (*bpf_ktime_get_boot_ns)(void) = (void *) 125; + +/* + * bpf_seq_printf + * + * **bpf_seq_printf**\ () uses seq_file **seq_printf**\ () to print + * out the format string. + * The *m* represents the seq_file. The *fmt* and *fmt_size* are for + * the format string itself. The *data* and *data_len* are format string + * arguments. The *data* are a **u64** array and corresponding format string + * values are stored in the array. For strings and pointers where pointees + * are accessed, only the pointer values are stored in the *data* array. + * The *data_len* is the size of *data* in bytes - must be a multiple of 8. + * + * Formats **%s**, **%p{i,I}{4,6}** requires to read kernel memory. + * Reading kernel memory may fail due to either invalid address or + * valid address but requiring a major memory fault. If reading kernel memory + * fails, the string for **%s** will be an empty string, and the ip + * address for **%p{i,I}{4,6}** will be 0. Not returning error to + * bpf program is consistent with what **bpf_trace_printk**\ () does for now. + * + * Returns + * 0 on success, or a negative error in case of failure: + * + * **-EBUSY** if per-CPU memory copy buffer is busy, can try again + * by returning 1 from bpf program. + * + * **-EINVAL** if arguments are invalid, or if *fmt* is invalid/unsupported. + * + * **-E2BIG** if *fmt* contains too many format specifiers. + * + * **-EOVERFLOW** if an overflow happened: The same object will be tried again. + */ +static long (*bpf_seq_printf)(struct seq_file *m, const char *fmt, __u32 fmt_size, const void *data, __u32 data_len) = (void *) 126; + +/* + * bpf_seq_write + * + * **bpf_seq_write**\ () uses seq_file **seq_write**\ () to write the data. + * The *m* represents the seq_file. The *data* and *len* represent the + * data to write in bytes. + * + * Returns + * 0 on success, or a negative error in case of failure: + * + * **-EOVERFLOW** if an overflow happened: The same object will be tried again. + */ +static long (*bpf_seq_write)(struct seq_file *m, const void *data, __u32 len) = (void *) 127; + +/* + * bpf_sk_cgroup_id + * + * Return the cgroup v2 id of the socket *sk*. + * + * *sk* must be a non-**NULL** pointer to a socket, e.g. one + * returned from **bpf_sk_lookup_xxx**\ (), + * **bpf_sk_fullsock**\ (), etc. The format of returned id is + * same as in **bpf_skb_cgroup_id**\ (). + * + * This helper is available only if the kernel was compiled with + * the **CONFIG_SOCK_CGROUP_DATA** configuration option. + * + * Returns + * The id is returned or 0 in case the id could not be retrieved. + */ +static __u64 (*bpf_sk_cgroup_id)(void *sk) = (void *) 128; + +/* + * bpf_sk_ancestor_cgroup_id + * + * Return id of cgroup v2 that is ancestor of cgroup associated + * with the *sk* at the *ancestor_level*. The root cgroup is at + * *ancestor_level* zero and each step down the hierarchy + * increments the level. If *ancestor_level* == level of cgroup + * associated with *sk*, then return value will be same as that + * of **bpf_sk_cgroup_id**\ (). + * + * The helper is useful to implement policies based on cgroups + * that are upper in hierarchy than immediate cgroup associated + * with *sk*. + * + * The format of returned id and helper limitations are same as in + * **bpf_sk_cgroup_id**\ (). + * + * Returns + * The id is returned or 0 in case the id could not be retrieved. + */ +static __u64 (*bpf_sk_ancestor_cgroup_id)(void *sk, int ancestor_level) = (void *) 129; + +/* + * bpf_ringbuf_output + * + * Copy *size* bytes from *data* into a ring buffer *ringbuf*. + * If **BPF_RB_NO_WAKEUP** is specified in *flags*, no notification + * of new data availability is sent. + * If **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification + * of new data availability is sent unconditionally. + * If **0** is specified in *flags*, an adaptive notification + * of new data availability is sent. + * + * An adaptive notification is a notification sent whenever the user-space + * process has caught up and consumed all available payloads. In case the user-space + * process is still processing a previous payload, then no notification is needed + * as it will process the newly added payload automatically. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_ringbuf_output)(void *ringbuf, void *data, __u64 size, __u64 flags) = (void *) 130; + +/* + * bpf_ringbuf_reserve + * + * Reserve *size* bytes of payload in a ring buffer *ringbuf*. + * *flags* must be 0. + * + * Returns + * Valid pointer with *size* bytes of memory available; NULL, + * otherwise. + */ +static void *(*bpf_ringbuf_reserve)(void *ringbuf, __u64 size, __u64 flags) = (void *) 131; + +/* + * bpf_ringbuf_submit + * + * Submit reserved ring buffer sample, pointed to by *data*. + * If **BPF_RB_NO_WAKEUP** is specified in *flags*, no notification + * of new data availability is sent. + * If **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification + * of new data availability is sent unconditionally. + * If **0** is specified in *flags*, an adaptive notification + * of new data availability is sent. + * + * See 'bpf_ringbuf_output()' for the definition of adaptive notification. + * + * Returns + * Nothing. Always succeeds. + */ +static void (*bpf_ringbuf_submit)(void *data, __u64 flags) = (void *) 132; + +/* + * bpf_ringbuf_discard + * + * Discard reserved ring buffer sample, pointed to by *data*. + * If **BPF_RB_NO_WAKEUP** is specified in *flags*, no notification + * of new data availability is sent. + * If **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification + * of new data availability is sent unconditionally. + * If **0** is specified in *flags*, an adaptive notification + * of new data availability is sent. + * + * See 'bpf_ringbuf_output()' for the definition of adaptive notification. + * + * Returns + * Nothing. Always succeeds. + */ +static void (*bpf_ringbuf_discard)(void *data, __u64 flags) = (void *) 133; + +/* + * bpf_ringbuf_query + * + * Query various characteristics of provided ring buffer. What + * exactly is queries is determined by *flags*: + * + * * **BPF_RB_AVAIL_DATA**: Amount of data not yet consumed. + * * **BPF_RB_RING_SIZE**: The size of ring buffer. + * * **BPF_RB_CONS_POS**: Consumer position (can wrap around). + * * **BPF_RB_PROD_POS**: Producer(s) position (can wrap around). + * + * Data returned is just a momentary snapshot of actual values + * and could be inaccurate, so this facility should be used to + * power heuristics and for reporting, not to make 100% correct + * calculation. + * + * Returns + * Requested value, or 0, if *flags* are not recognized. + */ +static __u64 (*bpf_ringbuf_query)(void *ringbuf, __u64 flags) = (void *) 134; + +/* + * bpf_csum_level + * + * Change the skbs checksum level by one layer up or down, or + * reset it entirely to none in order to have the stack perform + * checksum validation. The level is applicable to the following + * protocols: TCP, UDP, GRE, SCTP, FCOE. For example, a decap of + * | ETH | IP | UDP | GUE | IP | TCP | into | ETH | IP | TCP | + * through **bpf_skb_adjust_room**\ () helper with passing in + * **BPF_F_ADJ_ROOM_NO_CSUM_RESET** flag would require one call + * to **bpf_csum_level**\ () with **BPF_CSUM_LEVEL_DEC** since + * the UDP header is removed. Similarly, an encap of the latter + * into the former could be accompanied by a helper call to + * **bpf_csum_level**\ () with **BPF_CSUM_LEVEL_INC** if the + * skb is still intended to be processed in higher layers of the + * stack instead of just egressing at tc. + * + * There are three supported level settings at this time: + * + * * **BPF_CSUM_LEVEL_INC**: Increases skb->csum_level for skbs + * with CHECKSUM_UNNECESSARY. + * * **BPF_CSUM_LEVEL_DEC**: Decreases skb->csum_level for skbs + * with CHECKSUM_UNNECESSARY. + * * **BPF_CSUM_LEVEL_RESET**: Resets skb->csum_level to 0 and + * sets CHECKSUM_NONE to force checksum validation by the stack. + * * **BPF_CSUM_LEVEL_QUERY**: No-op, returns the current + * skb->csum_level. + * + * Returns + * 0 on success, or a negative error in case of failure. In the + * case of **BPF_CSUM_LEVEL_QUERY**, the current skb->csum_level + * is returned or the error code -EACCES in case the skb is not + * subject to CHECKSUM_UNNECESSARY. + */ +static long (*bpf_csum_level)(struct __sk_buff *skb, __u64 level) = (void *) 135; + +/* + * bpf_skc_to_tcp6_sock + * + * Dynamically cast a *sk* pointer to a *tcp6_sock* pointer. + * + * Returns + * *sk* if casting is valid, or **NULL** otherwise. + */ +static struct tcp6_sock *(*bpf_skc_to_tcp6_sock)(void *sk) = (void *) 136; + +/* + * bpf_skc_to_tcp_sock + * + * Dynamically cast a *sk* pointer to a *tcp_sock* pointer. + * + * Returns + * *sk* if casting is valid, or **NULL** otherwise. + */ +static struct tcp_sock *(*bpf_skc_to_tcp_sock)(void *sk) = (void *) 137; + +/* + * bpf_skc_to_tcp_timewait_sock + * + * Dynamically cast a *sk* pointer to a *tcp_timewait_sock* pointer. + * + * Returns + * *sk* if casting is valid, or **NULL** otherwise. + */ +static struct tcp_timewait_sock *(*bpf_skc_to_tcp_timewait_sock)(void *sk) = (void *) 138; + +/* + * bpf_skc_to_tcp_request_sock + * + * Dynamically cast a *sk* pointer to a *tcp_request_sock* pointer. + * + * Returns + * *sk* if casting is valid, or **NULL** otherwise. + */ +static struct tcp_request_sock *(*bpf_skc_to_tcp_request_sock)(void *sk) = (void *) 139; + +/* + * bpf_skc_to_udp6_sock + * + * Dynamically cast a *sk* pointer to a *udp6_sock* pointer. + * + * Returns + * *sk* if casting is valid, or **NULL** otherwise. + */ +static struct udp6_sock *(*bpf_skc_to_udp6_sock)(void *sk) = (void *) 140; + +/* + * bpf_get_task_stack + * + * Return a user or a kernel stack in bpf program provided buffer. + * To achieve this, the helper needs *task*, which is a valid + * pointer to **struct task_struct**. To store the stacktrace, the + * bpf program provides *buf* with a nonnegative *size*. + * + * The last argument, *flags*, holds the number of stack frames to + * skip (from 0 to 255), masked with + * **BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set + * the following flags: + * + * **BPF_F_USER_STACK** + * Collect a user space stack instead of a kernel stack. + * **BPF_F_USER_BUILD_ID** + * Collect buildid+offset instead of ips for user stack, + * only valid if **BPF_F_USER_STACK** is also specified. + * + * **bpf_get_task_stack**\ () can collect up to + * **PERF_MAX_STACK_DEPTH** both kernel and user frames, subject + * to sufficient large buffer size. Note that + * this limit can be controlled with the **sysctl** program, and + * that it should be manually increased in order to profile long + * user stacks (such as stacks for Java programs). To do so, use: + * + * :: + * + * # sysctl kernel.perf_event_max_stack=<new value> + * + * Returns + * The non-negative copied *buf* length equal to or less than + * *size* on success, or a negative error in case of failure. + */ +static long (*bpf_get_task_stack)(struct task_struct *task, void *buf, __u32 size, __u64 flags) = (void *) 141; + +/* + * bpf_load_hdr_opt + * + * Load header option. Support reading a particular TCP header + * option for bpf program (**BPF_PROG_TYPE_SOCK_OPS**). + * + * If *flags* is 0, it will search the option from the + * *skops*\ **->skb_data**. The comment in **struct bpf_sock_ops** + * has details on what skb_data contains under different + * *skops*\ **->op**. + * + * The first byte of the *searchby_res* specifies the + * kind that it wants to search. + * + * If the searching kind is an experimental kind + * (i.e. 253 or 254 according to RFC6994). It also + * needs to specify the "magic" which is either + * 2 bytes or 4 bytes. It then also needs to + * specify the size of the magic by using + * the 2nd byte which is "kind-length" of a TCP + * header option and the "kind-length" also + * includes the first 2 bytes "kind" and "kind-length" + * itself as a normal TCP header option also does. + * + * For example, to search experimental kind 254 with + * 2 byte magic 0xeB9F, the searchby_res should be + * [ 254, 4, 0xeB, 0x9F, 0, 0, .... 0 ]. + * + * To search for the standard window scale option (3), + * the *searchby_res* should be [ 3, 0, 0, .... 0 ]. + * Note, kind-length must be 0 for regular option. + * + * Searching for No-Op (0) and End-of-Option-List (1) are + * not supported. + * + * *len* must be at least 2 bytes which is the minimal size + * of a header option. + * + * Supported flags: + * + * * **BPF_LOAD_HDR_OPT_TCP_SYN** to search from the + * saved_syn packet or the just-received syn packet. + * + * + * Returns + * > 0 when found, the header option is copied to *searchby_res*. + * The return value is the total length copied. On failure, a + * negative error code is returned: + * + * **-EINVAL** if a parameter is invalid. + * + * **-ENOMSG** if the option is not found. + * + * **-ENOENT** if no syn packet is available when + * **BPF_LOAD_HDR_OPT_TCP_SYN** is used. + * + * **-ENOSPC** if there is not enough space. Only *len* number of + * bytes are copied. + * + * **-EFAULT** on failure to parse the header options in the + * packet. + * + * **-EPERM** if the helper cannot be used under the current + * *skops*\ **->op**. + */ +static long (*bpf_load_hdr_opt)(struct bpf_sock_ops *skops, void *searchby_res, __u32 len, __u64 flags) = (void *) 142; + +/* + * bpf_store_hdr_opt + * + * Store header option. The data will be copied + * from buffer *from* with length *len* to the TCP header. + * + * The buffer *from* should have the whole option that + * includes the kind, kind-length, and the actual + * option data. The *len* must be at least kind-length + * long. The kind-length does not have to be 4 byte + * aligned. The kernel will take care of the padding + * and setting the 4 bytes aligned value to th->doff. + * + * This helper will check for duplicated option + * by searching the same option in the outgoing skb. + * + * This helper can only be called during + * **BPF_SOCK_OPS_WRITE_HDR_OPT_CB**. + * + * + * Returns + * 0 on success, or negative error in case of failure: + * + * **-EINVAL** If param is invalid. + * + * **-ENOSPC** if there is not enough space in the header. + * Nothing has been written + * + * **-EEXIST** if the option already exists. + * + * **-EFAULT** on failrue to parse the existing header options. + * + * **-EPERM** if the helper cannot be used under the current + * *skops*\ **->op**. + */ +static long (*bpf_store_hdr_opt)(struct bpf_sock_ops *skops, const void *from, __u32 len, __u64 flags) = (void *) 143; + +/* + * bpf_reserve_hdr_opt + * + * Reserve *len* bytes for the bpf header option. The + * space will be used by **bpf_store_hdr_opt**\ () later in + * **BPF_SOCK_OPS_WRITE_HDR_OPT_CB**. + * + * If **bpf_reserve_hdr_opt**\ () is called multiple times, + * the total number of bytes will be reserved. + * + * This helper can only be called during + * **BPF_SOCK_OPS_HDR_OPT_LEN_CB**. + * + * + * Returns + * 0 on success, or negative error in case of failure: + * + * **-EINVAL** if a parameter is invalid. + * + * **-ENOSPC** if there is not enough space in the header. + * + * **-EPERM** if the helper cannot be used under the current + * *skops*\ **->op**. + */ +static long (*bpf_reserve_hdr_opt)(struct bpf_sock_ops *skops, __u32 len, __u64 flags) = (void *) 144; + +/* + * bpf_inode_storage_get + * + * Get a bpf_local_storage from an *inode*. + * + * Logically, it could be thought of as getting the value from + * a *map* with *inode* as the **key**. From this + * perspective, the usage is not much different from + * **bpf_map_lookup_elem**\ (*map*, **&**\ *inode*) except this + * helper enforces the key must be an inode and the map must also + * be a **BPF_MAP_TYPE_INODE_STORAGE**. + * + * Underneath, the value is stored locally at *inode* instead of + * the *map*. The *map* is used as the bpf-local-storage + * "type". The bpf-local-storage "type" (i.e. the *map*) is + * searched against all bpf_local_storage residing at *inode*. + * + * An optional *flags* (**BPF_LOCAL_STORAGE_GET_F_CREATE**) can be + * used such that a new bpf_local_storage will be + * created if one does not exist. *value* can be used + * together with **BPF_LOCAL_STORAGE_GET_F_CREATE** to specify + * the initial value of a bpf_local_storage. If *value* is + * **NULL**, the new bpf_local_storage will be zero initialized. + * + * Returns + * A bpf_local_storage pointer is returned on success. + * + * **NULL** if not found or there was an error in adding + * a new bpf_local_storage. + */ +static void *(*bpf_inode_storage_get)(void *map, void *inode, void *value, __u64 flags) = (void *) 145; + +/* + * bpf_inode_storage_delete + * + * Delete a bpf_local_storage from an *inode*. + * + * Returns + * 0 on success. + * + * **-ENOENT** if the bpf_local_storage cannot be found. + */ +static int (*bpf_inode_storage_delete)(void *map, void *inode) = (void *) 146; + +/* + * bpf_d_path + * + * Return full path for given **struct path** object, which + * needs to be the kernel BTF *path* object. The path is + * returned in the provided buffer *buf* of size *sz* and + * is zero terminated. + * + * + * Returns + * On success, the strictly positive length of the string, + * including the trailing NUL character. On error, a negative + * value. + */ +static long (*bpf_d_path)(struct path *path, char *buf, __u32 sz) = (void *) 147; + +/* + * bpf_copy_from_user + * + * Read *size* bytes from user space address *user_ptr* and store + * the data in *dst*. This is a wrapper of **copy_from_user**\ (). + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_copy_from_user)(void *dst, __u32 size, const void *user_ptr) = (void *) 148; + +/* + * bpf_snprintf_btf + * + * Use BTF to store a string representation of *ptr*->ptr in *str*, + * using *ptr*->type_id. This value should specify the type + * that *ptr*->ptr points to. LLVM __builtin_btf_type_id(type, 1) + * can be used to look up vmlinux BTF type ids. Traversing the + * data structure using BTF, the type information and values are + * stored in the first *str_size* - 1 bytes of *str*. Safe copy of + * the pointer data is carried out to avoid kernel crashes during + * operation. Smaller types can use string space on the stack; + * larger programs can use map data to store the string + * representation. + * + * The string can be subsequently shared with userspace via + * bpf_perf_event_output() or ring buffer interfaces. + * bpf_trace_printk() is to be avoided as it places too small + * a limit on string size to be useful. + * + * *flags* is a combination of + * + * **BTF_F_COMPACT** + * no formatting around type information + * **BTF_F_NONAME** + * no struct/union member names/types + * **BTF_F_PTR_RAW** + * show raw (unobfuscated) pointer values; + * equivalent to printk specifier %px. + * **BTF_F_ZERO** + * show zero-valued struct/union members; they + * are not displayed by default + * + * + * Returns + * The number of bytes that were written (or would have been + * written if output had to be truncated due to string size), + * or a negative error in cases of failure. + */ +static long (*bpf_snprintf_btf)(char *str, __u32 str_size, struct btf_ptr *ptr, __u32 btf_ptr_size, __u64 flags) = (void *) 149; + +/* + * bpf_seq_printf_btf + * + * Use BTF to write to seq_write a string representation of + * *ptr*->ptr, using *ptr*->type_id as per bpf_snprintf_btf(). + * *flags* are identical to those used for bpf_snprintf_btf. + * + * Returns + * 0 on success or a negative error in case of failure. + */ +static long (*bpf_seq_printf_btf)(struct seq_file *m, struct btf_ptr *ptr, __u32 ptr_size, __u64 flags) = (void *) 150; + +/* + * bpf_skb_cgroup_classid + * + * See **bpf_get_cgroup_classid**\ () for the main description. + * This helper differs from **bpf_get_cgroup_classid**\ () in that + * the cgroup v1 net_cls class is retrieved only from the *skb*'s + * associated socket instead of the current process. + * + * Returns + * The id is returned or 0 in case the id could not be retrieved. + */ +static __u64 (*bpf_skb_cgroup_classid)(struct __sk_buff *skb) = (void *) 151; + +/* + * bpf_redirect_neigh + * + * Redirect the packet to another net device of index *ifindex* + * and fill in L2 addresses from neighboring subsystem. This helper + * is somewhat similar to **bpf_redirect**\ (), except that it + * populates L2 addresses as well, meaning, internally, the helper + * relies on the neighbor lookup for the L2 address of the nexthop. + * + * The helper will perform a FIB lookup based on the skb's + * networking header to get the address of the next hop, unless + * this is supplied by the caller in the *params* argument. The + * *plen* argument indicates the len of *params* and should be set + * to 0 if *params* is NULL. + * + * The *flags* argument is reserved and must be 0. The helper is + * currently only supported for tc BPF program types, and enabled + * for IPv4 and IPv6 protocols. + * + * Returns + * The helper returns **TC_ACT_REDIRECT** on success or + * **TC_ACT_SHOT** on error. + */ +static long (*bpf_redirect_neigh)(__u32 ifindex, struct bpf_redir_neigh *params, int plen, __u64 flags) = (void *) 152; + +/* + * bpf_per_cpu_ptr + * + * Take a pointer to a percpu ksym, *percpu_ptr*, and return a + * pointer to the percpu kernel variable on *cpu*. A ksym is an + * extern variable decorated with '__ksym'. For ksym, there is a + * global var (either static or global) defined of the same name + * in the kernel. The ksym is percpu if the global var is percpu. + * The returned pointer points to the global percpu var on *cpu*. + * + * bpf_per_cpu_ptr() has the same semantic as per_cpu_ptr() in the + * kernel, except that bpf_per_cpu_ptr() may return NULL. This + * happens if *cpu* is larger than nr_cpu_ids. The caller of + * bpf_per_cpu_ptr() must check the returned value. + * + * Returns + * A pointer pointing to the kernel percpu variable on *cpu*, or + * NULL, if *cpu* is invalid. + */ +static void *(*bpf_per_cpu_ptr)(const void *percpu_ptr, __u32 cpu) = (void *) 153; + +/* + * bpf_this_cpu_ptr + * + * Take a pointer to a percpu ksym, *percpu_ptr*, and return a + * pointer to the percpu kernel variable on this cpu. See the + * description of 'ksym' in **bpf_per_cpu_ptr**\ (). + * + * bpf_this_cpu_ptr() has the same semantic as this_cpu_ptr() in + * the kernel. Different from **bpf_per_cpu_ptr**\ (), it would + * never return NULL. + * + * Returns + * A pointer pointing to the kernel percpu variable on this cpu. + */ +static void *(*bpf_this_cpu_ptr)(const void *percpu_ptr) = (void *) 154; + +/* + * bpf_redirect_peer + * + * Redirect the packet to another net device of index *ifindex*. + * This helper is somewhat similar to **bpf_redirect**\ (), except + * that the redirection happens to the *ifindex*' peer device and + * the netns switch takes place from ingress to ingress without + * going through the CPU's backlog queue. + * + * The *flags* argument is reserved and must be 0. The helper is + * currently only supported for tc BPF program types at the ingress + * hook and for veth device types. The peer device must reside in a + * different network namespace. + * + * Returns + * The helper returns **TC_ACT_REDIRECT** on success or + * **TC_ACT_SHOT** on error. + */ +static long (*bpf_redirect_peer)(__u32 ifindex, __u64 flags) = (void *) 155; + +/* + * bpf_task_storage_get + * + * Get a bpf_local_storage from the *task*. + * + * Logically, it could be thought of as getting the value from + * a *map* with *task* as the **key**. From this + * perspective, the usage is not much different from + * **bpf_map_lookup_elem**\ (*map*, **&**\ *task*) except this + * helper enforces the key must be an task_struct and the map must also + * be a **BPF_MAP_TYPE_TASK_STORAGE**. + * + * Underneath, the value is stored locally at *task* instead of + * the *map*. The *map* is used as the bpf-local-storage + * "type". The bpf-local-storage "type" (i.e. the *map*) is + * searched against all bpf_local_storage residing at *task*. + * + * An optional *flags* (**BPF_LOCAL_STORAGE_GET_F_CREATE**) can be + * used such that a new bpf_local_storage will be + * created if one does not exist. *value* can be used + * together with **BPF_LOCAL_STORAGE_GET_F_CREATE** to specify + * the initial value of a bpf_local_storage. If *value* is + * **NULL**, the new bpf_local_storage will be zero initialized. + * + * Returns + * A bpf_local_storage pointer is returned on success. + * + * **NULL** if not found or there was an error in adding + * a new bpf_local_storage. + */ +static void *(*bpf_task_storage_get)(void *map, struct task_struct *task, void *value, __u64 flags) = (void *) 156; + +/* + * bpf_task_storage_delete + * + * Delete a bpf_local_storage from a *task*. + * + * Returns + * 0 on success. + * + * **-ENOENT** if the bpf_local_storage cannot be found. + */ +static long (*bpf_task_storage_delete)(void *map, struct task_struct *task) = (void *) 157; + +/* + * bpf_get_current_task_btf + * + * Return a BTF pointer to the "current" task. + * This pointer can also be used in helpers that accept an + * *ARG_PTR_TO_BTF_ID* of type *task_struct*. + * + * Returns + * Pointer to the current task. + */ +static struct task_struct *(*bpf_get_current_task_btf)(void) = (void *) 158; + +/* + * bpf_bprm_opts_set + * + * Set or clear certain options on *bprm*: + * + * **BPF_F_BPRM_SECUREEXEC** Set the secureexec bit + * which sets the **AT_SECURE** auxv for glibc. The bit + * is cleared if the flag is not specified. + * + * Returns + * **-EINVAL** if invalid *flags* are passed, zero otherwise. + */ +static long (*bpf_bprm_opts_set)(struct linux_binprm *bprm, __u64 flags) = (void *) 159; + +/* + * bpf_ktime_get_coarse_ns + * + * Return a coarse-grained version of the time elapsed since + * system boot, in nanoseconds. Does not include time the system + * was suspended. + * + * See: **clock_gettime**\ (**CLOCK_MONOTONIC_COARSE**) + * + * Returns + * Current *ktime*. + */ +static __u64 (*bpf_ktime_get_coarse_ns)(void) = (void *) 160; + +/* + * bpf_ima_inode_hash + * + * Returns the stored IMA hash of the *inode* (if it's avaialable). + * If the hash is larger than *size*, then only *size* + * bytes will be copied to *dst* + * + * Returns + * The **hash_algo** is returned on success, + * **-EOPNOTSUP** if IMA is disabled or **-EINVAL** if + * invalid arguments are passed. + */ +static long (*bpf_ima_inode_hash)(struct inode *inode, void *dst, __u32 size) = (void *) 161; + +/* + * bpf_sock_from_file + * + * If the given file represents a socket, returns the associated + * socket. + * + * Returns + * A pointer to a struct socket on success or NULL if the file is + * not a socket. + */ +static struct socket *(*bpf_sock_from_file)(struct file *file) = (void *) 162; + +/* + * bpf_check_mtu + * + * Check packet size against exceeding MTU of net device (based + * on *ifindex*). This helper will likely be used in combination + * with helpers that adjust/change the packet size. + * + * The argument *len_diff* can be used for querying with a planned + * size change. This allows to check MTU prior to changing packet + * ctx. Providing an *len_diff* adjustment that is larger than the + * actual packet size (resulting in negative packet size) will in + * principle not exceed the MTU, why it is not considered a + * failure. Other BPF-helpers are needed for performing the + * planned size change, why the responsability for catch a negative + * packet size belong in those helpers. + * + * Specifying *ifindex* zero means the MTU check is performed + * against the current net device. This is practical if this isn't + * used prior to redirect. + * + * On input *mtu_len* must be a valid pointer, else verifier will + * reject BPF program. If the value *mtu_len* is initialized to + * zero then the ctx packet size is use. When value *mtu_len* is + * provided as input this specify the L3 length that the MTU check + * is done against. Remember XDP and TC length operate at L2, but + * this value is L3 as this correlate to MTU and IP-header tot_len + * values which are L3 (similar behavior as bpf_fib_lookup). + * + * The Linux kernel route table can configure MTUs on a more + * specific per route level, which is not provided by this helper. + * For route level MTU checks use the **bpf_fib_lookup**\ () + * helper. + * + * *ctx* is either **struct xdp_md** for XDP programs or + * **struct sk_buff** for tc cls_act programs. + * + * The *flags* argument can be a combination of one or more of the + * following values: + * + * **BPF_MTU_CHK_SEGS** + * This flag will only works for *ctx* **struct sk_buff**. + * If packet context contains extra packet segment buffers + * (often knows as GSO skb), then MTU check is harder to + * check at this point, because in transmit path it is + * possible for the skb packet to get re-segmented + * (depending on net device features). This could still be + * a MTU violation, so this flag enables performing MTU + * check against segments, with a different violation + * return code to tell it apart. Check cannot use len_diff. + * + * On return *mtu_len* pointer contains the MTU value of the net + * device. Remember the net device configured MTU is the L3 size, + * which is returned here and XDP and TC length operate at L2. + * Helper take this into account for you, but remember when using + * MTU value in your BPF-code. + * + * + * Returns + * * 0 on success, and populate MTU value in *mtu_len* pointer. + * + * * < 0 if any input argument is invalid (*mtu_len* not updated) + * + * MTU violations return positive values, but also populate MTU + * value in *mtu_len* pointer, as this can be needed for + * implementing PMTU handing: + * + * * **BPF_MTU_CHK_RET_FRAG_NEEDED** + * * **BPF_MTU_CHK_RET_SEGS_TOOBIG** + */ +static long (*bpf_check_mtu)(void *ctx, __u32 ifindex, __u32 *mtu_len, __s32 len_diff, __u64 flags) = (void *) 163; + +/* + * bpf_for_each_map_elem + * + * For each element in **map**, call **callback_fn** function with + * **map**, **callback_ctx** and other map-specific parameters. + * The **callback_fn** should be a static function and + * the **callback_ctx** should be a pointer to the stack. + * The **flags** is used to control certain aspects of the helper. + * Currently, the **flags** must be 0. + * + * The following are a list of supported map types and their + * respective expected callback signatures: + * + * BPF_MAP_TYPE_HASH, BPF_MAP_TYPE_PERCPU_HASH, + * BPF_MAP_TYPE_LRU_HASH, BPF_MAP_TYPE_LRU_PERCPU_HASH, + * BPF_MAP_TYPE_ARRAY, BPF_MAP_TYPE_PERCPU_ARRAY + * + * long (\*callback_fn)(struct bpf_map \*map, const void \*key, void \*value, void \*ctx); + * + * For per_cpu maps, the map_value is the value on the cpu where the + * bpf_prog is running. + * + * If **callback_fn** return 0, the helper will continue to the next + * element. If return value is 1, the helper will skip the rest of + * elements and return. Other return values are not used now. + * + * + * Returns + * The number of traversed map elements for success, **-EINVAL** for + * invalid **flags**. + */ +static long (*bpf_for_each_map_elem)(void *map, void *callback_fn, void *callback_ctx, __u64 flags) = (void *) 164; + +/* + * bpf_snprintf + * + * Outputs a string into the **str** buffer of size **str_size** + * based on a format string stored in a read-only map pointed by + * **fmt**. + * + * Each format specifier in **fmt** corresponds to one u64 element + * in the **data** array. For strings and pointers where pointees + * are accessed, only the pointer values are stored in the *data* + * array. The *data_len* is the size of *data* in bytes - must be + * a multiple of 8. + * + * Formats **%s** and **%p{i,I}{4,6}** require to read kernel + * memory. Reading kernel memory may fail due to either invalid + * address or valid address but requiring a major memory fault. If + * reading kernel memory fails, the string for **%s** will be an + * empty string, and the ip address for **%p{i,I}{4,6}** will be 0. + * Not returning error to bpf program is consistent with what + * **bpf_trace_printk**\ () does for now. + * + * + * Returns + * The strictly positive length of the formatted string, including + * the trailing zero character. If the return value is greater than + * **str_size**, **str** contains a truncated string, guaranteed to + * be zero-terminated except when **str_size** is 0. + * + * Or **-EBUSY** if the per-CPU memory copy buffer is busy. + */ +static long (*bpf_snprintf)(char *str, __u32 str_size, const char *fmt, __u64 *data, __u32 data_len) = (void *) 165; + +/* + * bpf_sys_bpf + * + * Execute bpf syscall with given arguments. + * + * Returns + * A syscall result. + */ +static long (*bpf_sys_bpf)(__u32 cmd, void *attr, __u32 attr_size) = (void *) 166; + +/* + * bpf_btf_find_by_name_kind + * + * Find BTF type with given name and kind in vmlinux BTF or in module's BTFs. + * + * Returns + * Returns btf_id and btf_obj_fd in lower and upper 32 bits. + */ +static long (*bpf_btf_find_by_name_kind)(char *name, int name_sz, __u32 kind, int flags) = (void *) 167; + +/* + * bpf_sys_close + * + * Execute close syscall for given FD. + * + * Returns + * A syscall result. + */ +static long (*bpf_sys_close)(__u32 fd) = (void *) 168; + +/* + * bpf_timer_init + * + * Initialize the timer. + * First 4 bits of *flags* specify clockid. + * Only CLOCK_MONOTONIC, CLOCK_REALTIME, CLOCK_BOOTTIME are allowed. + * All other bits of *flags* are reserved. + * The verifier will reject the program if *timer* is not from + * the same *map*. + * + * Returns + * 0 on success. + * **-EBUSY** if *timer* is already initialized. + * **-EINVAL** if invalid *flags* are passed. + * **-EPERM** if *timer* is in a map that doesn't have any user references. + * The user space should either hold a file descriptor to a map with timers + * or pin such map in bpffs. When map is unpinned or file descriptor is + * closed all timers in the map will be cancelled and freed. + */ +static long (*bpf_timer_init)(struct bpf_timer *timer, void *map, __u64 flags) = (void *) 169; + +/* + * bpf_timer_set_callback + * + * Configure the timer to call *callback_fn* static function. + * + * Returns + * 0 on success. + * **-EINVAL** if *timer* was not initialized with bpf_timer_init() earlier. + * **-EPERM** if *timer* is in a map that doesn't have any user references. + * The user space should either hold a file descriptor to a map with timers + * or pin such map in bpffs. When map is unpinned or file descriptor is + * closed all timers in the map will be cancelled and freed. + */ +static long (*bpf_timer_set_callback)(struct bpf_timer *timer, void *callback_fn) = (void *) 170; + +/* + * bpf_timer_start + * + * Set timer expiration N nanoseconds from the current time. The + * configured callback will be invoked in soft irq context on some cpu + * and will not repeat unless another bpf_timer_start() is made. + * In such case the next invocation can migrate to a different cpu. + * Since struct bpf_timer is a field inside map element the map + * owns the timer. The bpf_timer_set_callback() will increment refcnt + * of BPF program to make sure that callback_fn code stays valid. + * When user space reference to a map reaches zero all timers + * in a map are cancelled and corresponding program's refcnts are + * decremented. This is done to make sure that Ctrl-C of a user + * process doesn't leave any timers running. If map is pinned in + * bpffs the callback_fn can re-arm itself indefinitely. + * bpf_map_update/delete_elem() helpers and user space sys_bpf commands + * cancel and free the timer in the given map element. + * The map can contain timers that invoke callback_fn-s from different + * programs. The same callback_fn can serve different timers from + * different maps if key/value layout matches across maps. + * Every bpf_timer_set_callback() can have different callback_fn. + * + * + * Returns + * 0 on success. + * **-EINVAL** if *timer* was not initialized with bpf_timer_init() earlier + * or invalid *flags* are passed. + */ +static long (*bpf_timer_start)(struct bpf_timer *timer, __u64 nsecs, __u64 flags) = (void *) 171; + +/* + * bpf_timer_cancel + * + * Cancel the timer and wait for callback_fn to finish if it was running. + * + * Returns + * 0 if the timer was not active. + * 1 if the timer was active. + * **-EINVAL** if *timer* was not initialized with bpf_timer_init() earlier. + * **-EDEADLK** if callback_fn tried to call bpf_timer_cancel() on its + * own timer which would have led to a deadlock otherwise. + */ +static long (*bpf_timer_cancel)(struct bpf_timer *timer) = (void *) 172; + +/* + * bpf_get_func_ip + * + * Get address of the traced function (for tracing and kprobe programs). + * + * Returns + * Address of the traced function. + */ +static __u64 (*bpf_get_func_ip)(void *ctx) = (void *) 173; + +/* + * bpf_get_attach_cookie + * + * Get bpf_cookie value provided (optionally) during the program + * attachment. It might be different for each individual + * attachment, even if BPF program itself is the same. + * Expects BPF program context *ctx* as a first argument. + * + * Supported for the following program types: + * - kprobe/uprobe; + * - tracepoint; + * - perf_event. + * + * Returns + * Value specified by user at BPF link creation/attachment time + * or 0, if it was not specified. + */ +static __u64 (*bpf_get_attach_cookie)(void *ctx) = (void *) 174; + +/* + * bpf_task_pt_regs + * + * Get the struct pt_regs associated with **task**. + * + * Returns + * A pointer to struct pt_regs. + */ +static long (*bpf_task_pt_regs)(struct task_struct *task) = (void *) 175; + +/* + * bpf_get_branch_snapshot + * + * Get branch trace from hardware engines like Intel LBR. The + * hardware engine is stopped shortly after the helper is + * called. Therefore, the user need to filter branch entries + * based on the actual use case. To capture branch trace + * before the trigger point of the BPF program, the helper + * should be called at the beginning of the BPF program. + * + * The data is stored as struct perf_branch_entry into output + * buffer *entries*. *size* is the size of *entries* in bytes. + * *flags* is reserved for now and must be zero. + * + * + * Returns + * On success, number of bytes written to *buf*. On error, a + * negative value. + * + * **-EINVAL** if *flags* is not zero. + * + * **-ENOENT** if architecture does not support branch records. + */ +static long (*bpf_get_branch_snapshot)(void *entries, __u32 size, __u64 flags) = (void *) 176; + +/* + * bpf_trace_vprintk + * + * Behaves like **bpf_trace_printk**\ () helper, but takes an array of u64 + * to format and can handle more format args as a result. + * + * Arguments are to be used as in **bpf_seq_printf**\ () helper. + * + * Returns + * The number of bytes written to the buffer, or a negative error + * in case of failure. + */ +static long (*bpf_trace_vprintk)(const char *fmt, __u32 fmt_size, const void *data, __u32 data_len) = (void *) 177; + +/* + * bpf_skc_to_unix_sock + * + * Dynamically cast a *sk* pointer to a *unix_sock* pointer. + * + * Returns + * *sk* if casting is valid, or **NULL** otherwise. + */ +static struct unix_sock *(*bpf_skc_to_unix_sock)(void *sk) = (void *) 178; + +/* + * bpf_kallsyms_lookup_name + * + * Get the address of a kernel symbol, returned in *res*. *res* is + * set to 0 if the symbol is not found. + * + * Returns + * On success, zero. On error, a negative value. + * + * **-EINVAL** if *flags* is not zero. + * + * **-EINVAL** if string *name* is not the same size as *name_sz*. + * + * **-ENOENT** if symbol is not found. + * + * **-EPERM** if caller does not have permission to obtain kernel address. + */ +static long (*bpf_kallsyms_lookup_name)(const char *name, int name_sz, int flags, __u64 *res) = (void *) 179; + +/* + * bpf_find_vma + * + * Find vma of *task* that contains *addr*, call *callback_fn* + * function with *task*, *vma*, and *callback_ctx*. + * The *callback_fn* should be a static function and + * the *callback_ctx* should be a pointer to the stack. + * The *flags* is used to control certain aspects of the helper. + * Currently, the *flags* must be 0. + * + * The expected callback signature is + * + * long (\*callback_fn)(struct task_struct \*task, struct vm_area_struct \*vma, void \*callback_ctx); + * + * + * Returns + * 0 on success. + * **-ENOENT** if *task->mm* is NULL, or no vma contains *addr*. + * **-EBUSY** if failed to try lock mmap_lock. + * **-EINVAL** for invalid **flags**. + */ +static long (*bpf_find_vma)(struct task_struct *task, __u64 addr, void *callback_fn, void *callback_ctx, __u64 flags) = (void *) 180; + +/* + * bpf_loop + * + * For **nr_loops**, call **callback_fn** function + * with **callback_ctx** as the context parameter. + * The **callback_fn** should be a static function and + * the **callback_ctx** should be a pointer to the stack. + * The **flags** is used to control certain aspects of the helper. + * Currently, the **flags** must be 0. Currently, nr_loops is + * limited to 1 << 23 (~8 million) loops. + * + * long (\*callback_fn)(u32 index, void \*ctx); + * + * where **index** is the current index in the loop. The index + * is zero-indexed. + * + * If **callback_fn** returns 0, the helper will continue to the next + * loop. If return value is 1, the helper will skip the rest of + * the loops and return. Other return values are not used now, + * and will be rejected by the verifier. + * + * + * Returns + * The number of loops performed, **-EINVAL** for invalid **flags**, + * **-E2BIG** if **nr_loops** exceeds the maximum number of loops. + */ +static long (*bpf_loop)(__u32 nr_loops, void *callback_fn, void *callback_ctx, __u64 flags) = (void *) 181; + +/* + * bpf_strncmp + * + * Do strncmp() between **s1** and **s2**. **s1** doesn't need + * to be null-terminated and **s1_sz** is the maximum storage + * size of **s1**. **s2** must be a read-only string. + * + * Returns + * An integer less than, equal to, or greater than zero + * if the first **s1_sz** bytes of **s1** is found to be + * less than, to match, or be greater than **s2**. + */ +static long (*bpf_strncmp)(const char *s1, __u32 s1_sz, const char *s2) = (void *) 182; + +/* + * bpf_get_func_arg + * + * Get **n**-th argument (zero based) of the traced function (for tracing programs) + * returned in **value**. + * + * + * Returns + * 0 on success. + * **-EINVAL** if n >= arguments count of traced function. + */ +static long (*bpf_get_func_arg)(void *ctx, __u32 n, __u64 *value) = (void *) 183; + +/* + * bpf_get_func_ret + * + * Get return value of the traced function (for tracing programs) + * in **value**. + * + * + * Returns + * 0 on success. + * **-EOPNOTSUPP** for tracing programs other than BPF_TRACE_FEXIT or BPF_MODIFY_RETURN. + */ +static long (*bpf_get_func_ret)(void *ctx, __u64 *value) = (void *) 184; + +/* + * bpf_get_func_arg_cnt + * + * Get number of arguments of the traced function (for tracing programs). + * + * + * Returns + * The number of arguments of the traced function. + */ +static long (*bpf_get_func_arg_cnt)(void *ctx) = (void *) 185; + +/* + * bpf_get_retval + * + * Get the syscall's return value that will be returned to userspace. + * + * This helper is currently supported by cgroup programs only. + * + * Returns + * The syscall's return value. + */ +static int (*bpf_get_retval)(void) = (void *) 186; + +/* + * bpf_set_retval + * + * Set the syscall's return value that will be returned to userspace. + * + * This helper is currently supported by cgroup programs only. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static int (*bpf_set_retval)(int retval) = (void *) 187; + +/* + * bpf_xdp_get_buff_len + * + * Get the total size of a given xdp buff (linear and paged area) + * + * Returns + * The total size of a given xdp buffer. + */ +static __u64 (*bpf_xdp_get_buff_len)(struct xdp_md *xdp_md) = (void *) 188; + +/* + * bpf_xdp_load_bytes + * + * This helper is provided as an easy way to load data from a + * xdp buffer. It can be used to load *len* bytes from *offset* from + * the frame associated to *xdp_md*, into the buffer pointed by + * *buf*. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_xdp_load_bytes)(struct xdp_md *xdp_md, __u32 offset, void *buf, __u32 len) = (void *) 189; + +/* + * bpf_xdp_store_bytes + * + * Store *len* bytes from buffer *buf* into the frame + * associated to *xdp_md*, at *offset*. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_xdp_store_bytes)(struct xdp_md *xdp_md, __u32 offset, void *buf, __u32 len) = (void *) 190; + +/* + * bpf_copy_from_user_task + * + * Read *size* bytes from user space address *user_ptr* in *tsk*'s + * address space, and stores the data in *dst*. *flags* is not + * used yet and is provided for future extensibility. This helper + * can only be used by sleepable programs. + * + * Returns + * 0 on success, or a negative error in case of failure. On error + * *dst* buffer is zeroed out. + */ +static long (*bpf_copy_from_user_task)(void *dst, __u32 size, const void *user_ptr, struct task_struct *tsk, __u64 flags) = (void *) 191; + +/* + * bpf_skb_set_tstamp + * + * Change the __sk_buff->tstamp_type to *tstamp_type* + * and set *tstamp* to the __sk_buff->tstamp together. + * + * If there is no need to change the __sk_buff->tstamp_type, + * the tstamp value can be directly written to __sk_buff->tstamp + * instead. + * + * BPF_SKB_TSTAMP_DELIVERY_MONO is the only tstamp that + * will be kept during bpf_redirect_*(). A non zero + * *tstamp* must be used with the BPF_SKB_TSTAMP_DELIVERY_MONO + * *tstamp_type*. + * + * A BPF_SKB_TSTAMP_UNSPEC *tstamp_type* can only be used + * with a zero *tstamp*. + * + * Only IPv4 and IPv6 skb->protocol are supported. + * + * This function is most useful when it needs to set a + * mono delivery time to __sk_buff->tstamp and then + * bpf_redirect_*() to the egress of an iface. For example, + * changing the (rcv) timestamp in __sk_buff->tstamp at + * ingress to a mono delivery time and then bpf_redirect_*() + * to sch_fq@phy-dev. + * + * Returns + * 0 on success. + * **-EINVAL** for invalid input + * **-EOPNOTSUPP** for unsupported protocol + */ +static long (*bpf_skb_set_tstamp)(struct __sk_buff *skb, __u64 tstamp, __u32 tstamp_type) = (void *) 192; + +/* + * bpf_ima_file_hash + * + * Returns a calculated IMA hash of the *file*. + * If the hash is larger than *size*, then only *size* + * bytes will be copied to *dst* + * + * Returns + * The **hash_algo** is returned on success, + * **-EOPNOTSUP** if the hash calculation failed or **-EINVAL** if + * invalid arguments are passed. + */ +static long (*bpf_ima_file_hash)(struct file *file, void *dst, __u32 size) = (void *) 193; + +/* + * bpf_kptr_xchg + * + * Exchange kptr at pointer *map_value* with *ptr*, and return the + * old value. *ptr* can be NULL, otherwise it must be a referenced + * pointer which will be released when this helper is called. + * + * Returns + * The old value of kptr (which can be NULL). The returned pointer + * if not NULL, is a reference which must be released using its + * corresponding release function, or moved into a BPF map before + * program exit. + */ +static void *(*bpf_kptr_xchg)(void *map_value, void *ptr) = (void *) 194; + +/* + * bpf_map_lookup_percpu_elem + * + * Perform a lookup in *percpu map* for an entry associated to + * *key* on *cpu*. + * + * Returns + * Map value associated to *key* on *cpu*, or **NULL** if no entry + * was found or *cpu* is invalid. + */ +static void *(*bpf_map_lookup_percpu_elem)(void *map, const void *key, __u32 cpu) = (void *) 195; + +/* + * bpf_skc_to_mptcp_sock + * + * Dynamically cast a *sk* pointer to a *mptcp_sock* pointer. + * + * Returns + * *sk* if casting is valid, or **NULL** otherwise. + */ +static struct mptcp_sock *(*bpf_skc_to_mptcp_sock)(void *sk) = (void *) 196; + +/* + * bpf_dynptr_from_mem + * + * Get a dynptr to local memory *data*. + * + * *data* must be a ptr to a map value. + * The maximum *size* supported is DYNPTR_MAX_SIZE. + * *flags* is currently unused. + * + * Returns + * 0 on success, -E2BIG if the size exceeds DYNPTR_MAX_SIZE, + * -EINVAL if flags is not 0. + */ +static long (*bpf_dynptr_from_mem)(void *data, __u32 size, __u64 flags, struct bpf_dynptr *ptr) = (void *) 197; + +/* + * bpf_ringbuf_reserve_dynptr + * + * Reserve *size* bytes of payload in a ring buffer *ringbuf* + * through the dynptr interface. *flags* must be 0. + * + * Please note that a corresponding bpf_ringbuf_submit_dynptr or + * bpf_ringbuf_discard_dynptr must be called on *ptr*, even if the + * reservation fails. This is enforced by the verifier. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_ringbuf_reserve_dynptr)(void *ringbuf, __u32 size, __u64 flags, struct bpf_dynptr *ptr) = (void *) 198; + +/* + * bpf_ringbuf_submit_dynptr + * + * Submit reserved ring buffer sample, pointed to by *data*, + * through the dynptr interface. This is a no-op if the dynptr is + * invalid/null. + * + * For more information on *flags*, please see + * 'bpf_ringbuf_submit'. + * + * Returns + * Nothing. Always succeeds. + */ +static void (*bpf_ringbuf_submit_dynptr)(struct bpf_dynptr *ptr, __u64 flags) = (void *) 199; + +/* + * bpf_ringbuf_discard_dynptr + * + * Discard reserved ring buffer sample through the dynptr + * interface. This is a no-op if the dynptr is invalid/null. + * + * For more information on *flags*, please see + * 'bpf_ringbuf_discard'. + * + * Returns + * Nothing. Always succeeds. + */ +static void (*bpf_ringbuf_discard_dynptr)(struct bpf_dynptr *ptr, __u64 flags) = (void *) 200; + +/* + * bpf_dynptr_read + * + * Read *len* bytes from *src* into *dst*, starting from *offset* + * into *src*. + * *flags* is currently unused. + * + * Returns + * 0 on success, -E2BIG if *offset* + *len* exceeds the length + * of *src*'s data, -EINVAL if *src* is an invalid dynptr or if + * *flags* is not 0. + */ +static long (*bpf_dynptr_read)(void *dst, __u32 len, struct bpf_dynptr *src, __u32 offset, __u64 flags) = (void *) 201; + +/* + * bpf_dynptr_write + * + * Write *len* bytes from *src* into *dst*, starting from *offset* + * into *dst*. + * *flags* is currently unused. + * + * Returns + * 0 on success, -E2BIG if *offset* + *len* exceeds the length + * of *dst*'s data, -EINVAL if *dst* is an invalid dynptr or if *dst* + * is a read-only dynptr or if *flags* is not 0. + */ +static long (*bpf_dynptr_write)(struct bpf_dynptr *dst, __u32 offset, void *src, __u32 len, __u64 flags) = (void *) 202; + +/* + * bpf_dynptr_data + * + * Get a pointer to the underlying dynptr data. + * + * *len* must be a statically known value. The returned data slice + * is invalidated whenever the dynptr is invalidated. + * + * Returns + * Pointer to the underlying dynptr data, NULL if the dynptr is + * read-only, if the dynptr is invalid, or if the offset and length + * is out of bounds. + */ +static void *(*bpf_dynptr_data)(struct bpf_dynptr *ptr, __u32 offset, __u32 len) = (void *) 203; + +/* + * bpf_tcp_raw_gen_syncookie_ipv4 + * + * Try to issue a SYN cookie for the packet with corresponding + * IPv4/TCP headers, *iph* and *th*, without depending on a + * listening socket. + * + * *iph* points to the IPv4 header. + * + * *th* points to the start of the TCP header, while *th_len* + * contains the length of the TCP header (at least + * **sizeof**\ (**struct tcphdr**)). + * + * Returns + * On success, lower 32 bits hold the generated SYN cookie in + * followed by 16 bits which hold the MSS value for that cookie, + * and the top 16 bits are unused. + * + * On failure, the returned value is one of the following: + * + * **-EINVAL** if *th_len* is invalid. + */ +static __s64 (*bpf_tcp_raw_gen_syncookie_ipv4)(struct iphdr *iph, struct tcphdr *th, __u32 th_len) = (void *) 204; + +/* + * bpf_tcp_raw_gen_syncookie_ipv6 + * + * Try to issue a SYN cookie for the packet with corresponding + * IPv6/TCP headers, *iph* and *th*, without depending on a + * listening socket. + * + * *iph* points to the IPv6 header. + * + * *th* points to the start of the TCP header, while *th_len* + * contains the length of the TCP header (at least + * **sizeof**\ (**struct tcphdr**)). + * + * Returns + * On success, lower 32 bits hold the generated SYN cookie in + * followed by 16 bits which hold the MSS value for that cookie, + * and the top 16 bits are unused. + * + * On failure, the returned value is one of the following: + * + * **-EINVAL** if *th_len* is invalid. + * + * **-EPROTONOSUPPORT** if CONFIG_IPV6 is not builtin. + */ +static __s64 (*bpf_tcp_raw_gen_syncookie_ipv6)(struct ipv6hdr *iph, struct tcphdr *th, __u32 th_len) = (void *) 205; + +/* + * bpf_tcp_raw_check_syncookie_ipv4 + * + * Check whether *iph* and *th* contain a valid SYN cookie ACK + * without depending on a listening socket. + * + * *iph* points to the IPv4 header. + * + * *th* points to the TCP header. + * + * Returns + * 0 if *iph* and *th* are a valid SYN cookie ACK. + * + * On failure, the returned value is one of the following: + * + * **-EACCES** if the SYN cookie is not valid. + */ +static long (*bpf_tcp_raw_check_syncookie_ipv4)(struct iphdr *iph, struct tcphdr *th) = (void *) 206; + +/* + * bpf_tcp_raw_check_syncookie_ipv6 + * + * Check whether *iph* and *th* contain a valid SYN cookie ACK + * without depending on a listening socket. + * + * *iph* points to the IPv6 header. + * + * *th* points to the TCP header. + * + * Returns + * 0 if *iph* and *th* are a valid SYN cookie ACK. + * + * On failure, the returned value is one of the following: + * + * **-EACCES** if the SYN cookie is not valid. + * + * **-EPROTONOSUPPORT** if CONFIG_IPV6 is not builtin. + */ +static long (*bpf_tcp_raw_check_syncookie_ipv6)(struct ipv6hdr *iph, struct tcphdr *th) = (void *) 207; + + diff --git a/ebpf_prog/bpf_headers/bpf_helpers.h b/ebpf_prog/bpf_headers/bpf_helpers.h new file mode 100644 index 0000000..d0855b2 --- /dev/null +++ b/ebpf_prog/bpf_headers/bpf_helpers.h @@ -0,0 +1,301 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ +#ifndef __BPF_HELPERS__ +#define __BPF_HELPERS__ + +/* + * Note that bpf programs need to include either + * vmlinux.h (auto-generated from BTF) or linux/types.h + * in advance since bpf_helper_defs.h uses such types + * as __u64. + */ +#include "bpf_helper_defs.h" + +#define __uint(name, val) int (*name)[val] +#define __type(name, val) typeof(val) *name +#define __array(name, val) typeof(val) *name[] + +/* + * Helper macro to place programs, maps, license in + * different sections in elf_bpf file. Section names + * are interpreted by libbpf depending on the context (BPF programs, BPF maps, + * extern variables, etc). + * To allow use of SEC() with externs (e.g., for extern .maps declarations), + * make sure __attribute__((unused)) doesn't trigger compilation warning. + */ +#if __GNUC__ && !__clang__ + +/* + * Pragma macros are broken on GCC + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55578 + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90400 + */ +#define SEC(name) __attribute__((section(name), used)) + +#else + +#define SEC(name) \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wignored-attributes\"") \ + __attribute__((section(name), used)) \ + _Pragma("GCC diagnostic pop") \ + +#endif + +/* Avoid 'linux/stddef.h' definition of '__always_inline'. */ +#undef __always_inline +#define __always_inline inline __attribute__((always_inline)) + +#ifndef __noinline +#define __noinline __attribute__((noinline)) +#endif +#ifndef __weak +#define __weak __attribute__((weak)) +#endif + +/* + * Use __hidden attribute to mark a non-static BPF subprogram effectively + * static for BPF verifier's verification algorithm purposes, allowing more + * extensive and permissive BPF verification process, taking into account + * subprogram's caller context. + */ +#define __hidden __attribute__((visibility("hidden"))) + +/* When utilizing vmlinux.h with BPF CO-RE, user BPF programs can't include + * any system-level headers (such as stddef.h, linux/version.h, etc), and + * commonly-used macros like NULL and KERNEL_VERSION aren't available through + * vmlinux.h. This just adds unnecessary hurdles and forces users to re-define + * them on their own. So as a convenience, provide such definitions here. + */ +#ifndef NULL +#define NULL ((void *)0) +#endif + +#ifndef KERNEL_VERSION +#define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + ((c) > 255 ? 255 : (c))) +#endif + +/* + * Helper macros to manipulate data structures + */ +#ifndef offsetof +#define offsetof(TYPE, MEMBER) ((unsigned long)&((TYPE *)0)->MEMBER) +#endif +#ifndef container_of +#define container_of(ptr, type, member) \ + ({ \ + void *__mptr = (void *)(ptr); \ + ((type *)(__mptr - offsetof(type, member))); \ + }) +#endif + +/* + * Compiler (optimization) barrier. + */ +#ifndef barrier +#define barrier() asm volatile("" ::: "memory") +#endif + +/* Variable-specific compiler (optimization) barrier. It's a no-op which makes + * compiler believe that there is some black box modification of a given + * variable and thus prevents compiler from making extra assumption about its + * value and potential simplifications and optimizations on this variable. + * + * E.g., compiler might often delay or even omit 32-bit to 64-bit casting of + * a variable, making some code patterns unverifiable. Putting barrier_var() + * in place will ensure that cast is performed before the barrier_var() + * invocation, because compiler has to pessimistically assume that embedded + * asm section might perform some extra operations on that variable. + * + * This is a variable-specific variant of more global barrier(). + */ +#ifndef barrier_var +#define barrier_var(var) asm volatile("" : "=r"(var) : "0"(var)) +#endif + +/* + * Helper macro to throw a compilation error if __bpf_unreachable() gets + * built into the resulting code. This works given BPF back end does not + * implement __builtin_trap(). This is useful to assert that certain paths + * of the program code are never used and hence eliminated by the compiler. + * + * For example, consider a switch statement that covers known cases used by + * the program. __bpf_unreachable() can then reside in the default case. If + * the program gets extended such that a case is not covered in the switch + * statement, then it will throw a build error due to the default case not + * being compiled out. + */ +#ifndef __bpf_unreachable +# define __bpf_unreachable() __builtin_trap() +#endif + +/* + * Helper function to perform a tail call with a constant/immediate map slot. + */ +#if __clang_major__ >= 8 && defined(__bpf__) +static __always_inline void +bpf_tail_call_static(void *ctx, const void *map, const __u32 slot) +{ + if (!__builtin_constant_p(slot)) + __bpf_unreachable(); + + /* + * Provide a hard guarantee that LLVM won't optimize setting r2 (map + * pointer) and r3 (constant map index) from _different paths_ ending + * up at the _same_ call insn as otherwise we won't be able to use the + * jmpq/nopl retpoline-free patching by the x86-64 JIT in the kernel + * given they mismatch. See also d2e4c1e6c294 ("bpf: Constant map key + * tracking for prog array pokes") for details on verifier tracking. + * + * Note on clobber list: we need to stay in-line with BPF calling + * convention, so even if we don't end up using r0, r4, r5, we need + * to mark them as clobber so that LLVM doesn't end up using them + * before / after the call. + */ + asm volatile("r1 = %[ctx]\n\t" + "r2 = %[map]\n\t" + "r3 = %[slot]\n\t" + "call 12" + :: [ctx]"r"(ctx), [map]"r"(map), [slot]"i"(slot) + : "r0", "r1", "r2", "r3", "r4", "r5"); +} +#endif + +/* + * Helper structure used by eBPF C program + * to describe BPF map attributes to libbpf loader + */ +struct bpf_map_defold { + unsigned int type; + unsigned int key_size; + unsigned int value_size; + unsigned int max_entries; + unsigned int map_flags; +} __attribute__((deprecated("use BTF-defined maps in .maps section"))); + +enum libbpf_pin_type { + LIBBPF_PIN_NONE, + /* PIN_BY_NAME: pin maps by name (in /sys/fs/bpf by default) */ + LIBBPF_PIN_BY_NAME, +}; + +enum libbpf_tristate { + TRI_NO = 0, + TRI_YES = 1, + TRI_MODULE = 2, +}; + +#define __kconfig __attribute__((section(".kconfig"))) +#define __ksym __attribute__((section(".ksyms"))) +#define __kptr __attribute__((btf_type_tag("kptr"))) +#define __kptr_ref __attribute__((btf_type_tag("kptr_ref"))) + +#ifndef ___bpf_concat +#define ___bpf_concat(a, b) a ## b +#endif +#ifndef ___bpf_apply +#define ___bpf_apply(fn, n) ___bpf_concat(fn, n) +#endif +#ifndef ___bpf_nth +#define ___bpf_nth(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, _a, _b, _c, N, ...) N +#endif +#ifndef ___bpf_narg +#define ___bpf_narg(...) \ + ___bpf_nth(_, ##__VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +#endif + +#define ___bpf_fill0(arr, p, x) do {} while (0) +#define ___bpf_fill1(arr, p, x) arr[p] = x +#define ___bpf_fill2(arr, p, x, args...) arr[p] = x; ___bpf_fill1(arr, p + 1, args) +#define ___bpf_fill3(arr, p, x, args...) arr[p] = x; ___bpf_fill2(arr, p + 1, args) +#define ___bpf_fill4(arr, p, x, args...) arr[p] = x; ___bpf_fill3(arr, p + 1, args) +#define ___bpf_fill5(arr, p, x, args...) arr[p] = x; ___bpf_fill4(arr, p + 1, args) +#define ___bpf_fill6(arr, p, x, args...) arr[p] = x; ___bpf_fill5(arr, p + 1, args) +#define ___bpf_fill7(arr, p, x, args...) arr[p] = x; ___bpf_fill6(arr, p + 1, args) +#define ___bpf_fill8(arr, p, x, args...) arr[p] = x; ___bpf_fill7(arr, p + 1, args) +#define ___bpf_fill9(arr, p, x, args...) arr[p] = x; ___bpf_fill8(arr, p + 1, args) +#define ___bpf_fill10(arr, p, x, args...) arr[p] = x; ___bpf_fill9(arr, p + 1, args) +#define ___bpf_fill11(arr, p, x, args...) arr[p] = x; ___bpf_fill10(arr, p + 1, args) +#define ___bpf_fill12(arr, p, x, args...) arr[p] = x; ___bpf_fill11(arr, p + 1, args) +#define ___bpf_fill(arr, args...) \ + ___bpf_apply(___bpf_fill, ___bpf_narg(args))(arr, 0, args) + +/* + * BPF_SEQ_PRINTF to wrap bpf_seq_printf to-be-printed values + * in a structure. + */ +#define BPF_SEQ_PRINTF(seq, fmt, args...) \ +({ \ + static const char ___fmt[] = fmt; \ + unsigned long long ___param[___bpf_narg(args)]; \ + \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ + ___bpf_fill(___param, args); \ + _Pragma("GCC diagnostic pop") \ + \ + bpf_seq_printf(seq, ___fmt, sizeof(___fmt), \ + ___param, sizeof(___param)); \ +}) + +/* + * BPF_SNPRINTF wraps the bpf_snprintf helper with variadic arguments instead of + * an array of u64. + */ +#define BPF_SNPRINTF(out, out_size, fmt, args...) \ +({ \ + static const char ___fmt[] = fmt; \ + unsigned long long ___param[___bpf_narg(args)]; \ + \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ + ___bpf_fill(___param, args); \ + _Pragma("GCC diagnostic pop") \ + \ + bpf_snprintf(out, out_size, ___fmt, \ + ___param, sizeof(___param)); \ +}) + +#ifdef BPF_NO_GLOBAL_DATA +#define BPF_PRINTK_FMT_MOD +#else +#define BPF_PRINTK_FMT_MOD static const +#endif + +#define __bpf_printk(fmt, ...) \ +({ \ + BPF_PRINTK_FMT_MOD char ____fmt[] = fmt; \ + bpf_trace_printk(____fmt, sizeof(____fmt), \ + ##__VA_ARGS__); \ +}) + +/* + * __bpf_vprintk wraps the bpf_trace_vprintk helper with variadic arguments + * instead of an array of u64. + */ +#define __bpf_vprintk(fmt, args...) \ +({ \ + static const char ___fmt[] = fmt; \ + unsigned long long ___param[___bpf_narg(args)]; \ + \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ + ___bpf_fill(___param, args); \ + _Pragma("GCC diagnostic pop") \ + \ + bpf_trace_vprintk(___fmt, sizeof(___fmt), \ + ___param, sizeof(___param)); \ +}) + +/* Use __bpf_printk when bpf_printk call has 3 or fewer fmt args + * Otherwise use __bpf_vprintk + */ +#define ___bpf_pick_printk(...) \ + ___bpf_nth(_, ##__VA_ARGS__, __bpf_vprintk, __bpf_vprintk, __bpf_vprintk, \ + __bpf_vprintk, __bpf_vprintk, __bpf_vprintk, __bpf_vprintk, \ + __bpf_vprintk, __bpf_vprintk, __bpf_printk /*3*/, __bpf_printk /*2*/,\ + __bpf_printk /*1*/, __bpf_printk /*0*/) + +/* Helper macro to print out debug messages */ +#define bpf_printk(fmt, args...) ___bpf_pick_printk(args)(fmt, ##args) + +#endif diff --git a/ebpf_prog/bpf_headers/bpf_tracing.h b/ebpf_prog/bpf_headers/bpf_tracing.h new file mode 100644 index 0000000..1bc0c95 --- /dev/null +++ b/ebpf_prog/bpf_headers/bpf_tracing.h @@ -0,0 +1,563 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ +#ifndef __BPF_TRACING_H__ +#define __BPF_TRACING_H__ + +#include "bpf_helpers.h" + +/* Scan the ARCH passed in from ARCH env variable (see Makefile) */ +#if defined(__TARGET_ARCH_x86) + #define bpf_target_x86 + #define bpf_target_defined +#elif defined(__TARGET_ARCH_s390) + #define bpf_target_s390 + #define bpf_target_defined +#elif defined(__TARGET_ARCH_arm) + #define bpf_target_arm + #define bpf_target_defined +#elif defined(__TARGET_ARCH_arm64) + #define bpf_target_arm64 + #define bpf_target_defined +#elif defined(__TARGET_ARCH_mips) + #define bpf_target_mips + #define bpf_target_defined +#elif defined(__TARGET_ARCH_powerpc) + #define bpf_target_powerpc + #define bpf_target_defined +#elif defined(__TARGET_ARCH_sparc) + #define bpf_target_sparc + #define bpf_target_defined +#elif defined(__TARGET_ARCH_riscv) + #define bpf_target_riscv + #define bpf_target_defined +#elif defined(__TARGET_ARCH_arc) + #define bpf_target_arc + #define bpf_target_defined +#else + +/* Fall back to what the compiler says */ +#if defined(__x86_64__) + #define bpf_target_x86 + #define bpf_target_defined +#elif defined(__s390__) + #define bpf_target_s390 + #define bpf_target_defined +#elif defined(__arm__) + #define bpf_target_arm + #define bpf_target_defined +#elif defined(__aarch64__) + #define bpf_target_arm64 + #define bpf_target_defined +#elif defined(__mips__) + #define bpf_target_mips + #define bpf_target_defined +#elif defined(__powerpc__) + #define bpf_target_powerpc + #define bpf_target_defined +#elif defined(__sparc__) + #define bpf_target_sparc + #define bpf_target_defined +#elif defined(__riscv) && __riscv_xlen == 64 + #define bpf_target_riscv + #define bpf_target_defined +#elif defined(__arc__) + #define bpf_target_arc + #define bpf_target_defined +#endif /* no compiler target */ + +#endif + +#ifndef __BPF_TARGET_MISSING +#define __BPF_TARGET_MISSING "GCC error \"Must specify a BPF target arch via __TARGET_ARCH_xxx\"" +#endif + +#if defined(bpf_target_x86) + +#if defined(__KERNEL__) || defined(__VMLINUX_H__) + +#define __PT_PARM1_REG di +#define __PT_PARM2_REG si +#define __PT_PARM3_REG dx +#define __PT_PARM4_REG cx +#define __PT_PARM5_REG r8 +#define __PT_RET_REG sp +#define __PT_FP_REG bp +#define __PT_RC_REG ax +#define __PT_SP_REG sp +#define __PT_IP_REG ip +/* syscall uses r10 for PARM4 */ +#define PT_REGS_PARM4_SYSCALL(x) ((x)->r10) +#define PT_REGS_PARM4_CORE_SYSCALL(x) BPF_CORE_READ(x, r10) + +#else + +#ifdef __i386__ + +#define __PT_PARM1_REG eax +#define __PT_PARM2_REG edx +#define __PT_PARM3_REG ecx +/* i386 kernel is built with -mregparm=3 */ +#define __PT_PARM4_REG __unsupported__ +#define __PT_PARM5_REG __unsupported__ +#define __PT_RET_REG esp +#define __PT_FP_REG ebp +#define __PT_RC_REG eax +#define __PT_SP_REG esp +#define __PT_IP_REG eip + +#else /* __i386__ */ + +#define __PT_PARM1_REG rdi +#define __PT_PARM2_REG rsi +#define __PT_PARM3_REG rdx +#define __PT_PARM4_REG rcx +#define __PT_PARM5_REG r8 +#define __PT_RET_REG rsp +#define __PT_FP_REG rbp +#define __PT_RC_REG rax +#define __PT_SP_REG rsp +#define __PT_IP_REG rip +/* syscall uses r10 for PARM4 */ +#define PT_REGS_PARM4_SYSCALL(x) ((x)->r10) +#define PT_REGS_PARM4_CORE_SYSCALL(x) BPF_CORE_READ(x, r10) + +#endif /* __i386__ */ + +#endif /* __KERNEL__ || __VMLINUX_H__ */ + +#elif defined(bpf_target_s390) + +struct pt_regs___s390 { + unsigned long orig_gpr2; +}; + +/* s390 provides user_pt_regs instead of struct pt_regs to userspace */ +#define __PT_REGS_CAST(x) ((const user_pt_regs *)(x)) +#define __PT_PARM1_REG gprs[2] +#define __PT_PARM2_REG gprs[3] +#define __PT_PARM3_REG gprs[4] +#define __PT_PARM4_REG gprs[5] +#define __PT_PARM5_REG gprs[6] +#define __PT_RET_REG grps[14] +#define __PT_FP_REG gprs[11] /* Works only with CONFIG_FRAME_POINTER */ +#define __PT_RC_REG gprs[2] +#define __PT_SP_REG gprs[15] +#define __PT_IP_REG psw.addr +#define PT_REGS_PARM1_SYSCALL(x) PT_REGS_PARM1_CORE_SYSCALL(x) +#define PT_REGS_PARM1_CORE_SYSCALL(x) BPF_CORE_READ((const struct pt_regs___s390 *)(x), orig_gpr2) + +#elif defined(bpf_target_arm) + +#define __PT_PARM1_REG uregs[0] +#define __PT_PARM2_REG uregs[1] +#define __PT_PARM3_REG uregs[2] +#define __PT_PARM4_REG uregs[3] +#define __PT_PARM5_REG uregs[4] +#define __PT_RET_REG uregs[14] +#define __PT_FP_REG uregs[11] /* Works only with CONFIG_FRAME_POINTER */ +#define __PT_RC_REG uregs[0] +#define __PT_SP_REG uregs[13] +#define __PT_IP_REG uregs[12] + +#elif defined(bpf_target_arm64) + +struct pt_regs___arm64 { + unsigned long orig_x0; +}; + +/* arm64 provides struct user_pt_regs instead of struct pt_regs to userspace */ +#define __PT_REGS_CAST(x) ((const struct user_pt_regs *)(x)) +#define __PT_PARM1_REG regs[0] +#define __PT_PARM2_REG regs[1] +#define __PT_PARM3_REG regs[2] +#define __PT_PARM4_REG regs[3] +#define __PT_PARM5_REG regs[4] +#define __PT_RET_REG regs[30] +#define __PT_FP_REG regs[29] /* Works only with CONFIG_FRAME_POINTER */ +#define __PT_RC_REG regs[0] +#define __PT_SP_REG sp +#define __PT_IP_REG pc +#define PT_REGS_PARM1_SYSCALL(x) PT_REGS_PARM1_CORE_SYSCALL(x) +#define PT_REGS_PARM1_CORE_SYSCALL(x) BPF_CORE_READ((const struct pt_regs___arm64 *)(x), orig_x0) + +#elif defined(bpf_target_mips) + +#define __PT_PARM1_REG regs[4] +#define __PT_PARM2_REG regs[5] +#define __PT_PARM3_REG regs[6] +#define __PT_PARM4_REG regs[7] +#define __PT_PARM5_REG regs[8] +#define __PT_RET_REG regs[31] +#define __PT_FP_REG regs[30] /* Works only with CONFIG_FRAME_POINTER */ +#define __PT_RC_REG regs[2] +#define __PT_SP_REG regs[29] +#define __PT_IP_REG cp0_epc + +#elif defined(bpf_target_powerpc) + +#define __PT_PARM1_REG gpr[3] +#define __PT_PARM2_REG gpr[4] +#define __PT_PARM3_REG gpr[5] +#define __PT_PARM4_REG gpr[6] +#define __PT_PARM5_REG gpr[7] +#define __PT_RET_REG regs[31] +#define __PT_FP_REG __unsupported__ +#define __PT_RC_REG gpr[3] +#define __PT_SP_REG sp +#define __PT_IP_REG nip +/* powerpc does not select ARCH_HAS_SYSCALL_WRAPPER. */ +#define PT_REGS_SYSCALL_REGS(ctx) ctx + +#elif defined(bpf_target_sparc) + +#define __PT_PARM1_REG u_regs[UREG_I0] +#define __PT_PARM2_REG u_regs[UREG_I1] +#define __PT_PARM3_REG u_regs[UREG_I2] +#define __PT_PARM4_REG u_regs[UREG_I3] +#define __PT_PARM5_REG u_regs[UREG_I4] +#define __PT_RET_REG u_regs[UREG_I7] +#define __PT_FP_REG __unsupported__ +#define __PT_RC_REG u_regs[UREG_I0] +#define __PT_SP_REG u_regs[UREG_FP] +/* Should this also be a bpf_target check for the sparc case? */ +#if defined(__arch64__) +#define __PT_IP_REG tpc +#else +#define __PT_IP_REG pc +#endif + +#elif defined(bpf_target_riscv) + +#define __PT_REGS_CAST(x) ((const struct user_regs_struct *)(x)) +#define __PT_PARM1_REG a0 +#define __PT_PARM2_REG a1 +#define __PT_PARM3_REG a2 +#define __PT_PARM4_REG a3 +#define __PT_PARM5_REG a4 +#define __PT_RET_REG ra +#define __PT_FP_REG s0 +#define __PT_RC_REG a0 +#define __PT_SP_REG sp +#define __PT_IP_REG pc +/* riscv does not select ARCH_HAS_SYSCALL_WRAPPER. */ +#define PT_REGS_SYSCALL_REGS(ctx) ctx + +#elif defined(bpf_target_arc) + +/* arc provides struct user_pt_regs instead of struct pt_regs to userspace */ +#define __PT_REGS_CAST(x) ((const struct user_regs_struct *)(x)) +#define __PT_PARM1_REG scratch.r0 +#define __PT_PARM2_REG scratch.r1 +#define __PT_PARM3_REG scratch.r2 +#define __PT_PARM4_REG scratch.r3 +#define __PT_PARM5_REG scratch.r4 +#define __PT_RET_REG scratch.blink +#define __PT_FP_REG __unsupported__ +#define __PT_RC_REG scratch.r0 +#define __PT_SP_REG scratch.sp +#define __PT_IP_REG scratch.ret +/* arc does not select ARCH_HAS_SYSCALL_WRAPPER. */ +#define PT_REGS_SYSCALL_REGS(ctx) ctx + +#endif + +#if defined(bpf_target_defined) + +struct pt_regs; + +/* allow some architecutres to override `struct pt_regs` */ +#ifndef __PT_REGS_CAST +#define __PT_REGS_CAST(x) (x) +#endif + +#define PT_REGS_PARM1(x) (__PT_REGS_CAST(x)->__PT_PARM1_REG) +#define PT_REGS_PARM2(x) (__PT_REGS_CAST(x)->__PT_PARM2_REG) +#define PT_REGS_PARM3(x) (__PT_REGS_CAST(x)->__PT_PARM3_REG) +#define PT_REGS_PARM4(x) (__PT_REGS_CAST(x)->__PT_PARM4_REG) +#define PT_REGS_PARM5(x) (__PT_REGS_CAST(x)->__PT_PARM5_REG) +#define PT_REGS_RET(x) (__PT_REGS_CAST(x)->__PT_RET_REG) +#define PT_REGS_FP(x) (__PT_REGS_CAST(x)->__PT_FP_REG) +#define PT_REGS_RC(x) (__PT_REGS_CAST(x)->__PT_RC_REG) +#define PT_REGS_SP(x) (__PT_REGS_CAST(x)->__PT_SP_REG) +#define PT_REGS_IP(x) (__PT_REGS_CAST(x)->__PT_IP_REG) + +#define PT_REGS_PARM1_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM1_REG) +#define PT_REGS_PARM2_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM2_REG) +#define PT_REGS_PARM3_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM3_REG) +#define PT_REGS_PARM4_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM4_REG) +#define PT_REGS_PARM5_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM5_REG) +#define PT_REGS_RET_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_RET_REG) +#define PT_REGS_FP_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_FP_REG) +#define PT_REGS_RC_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_RC_REG) +#define PT_REGS_SP_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_SP_REG) +#define PT_REGS_IP_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_IP_REG) + +#if defined(bpf_target_powerpc) + +#define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = (ctx)->link; }) +#define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP + +#elif defined(bpf_target_sparc) + +#define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = PT_REGS_RET(ctx); }) +#define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP + +#else + +#define BPF_KPROBE_READ_RET_IP(ip, ctx) \ + ({ bpf_probe_read_kernel(&(ip), sizeof(ip), (void *)PT_REGS_RET(ctx)); }) +#define BPF_KRETPROBE_READ_RET_IP(ip, ctx) \ + ({ bpf_probe_read_kernel(&(ip), sizeof(ip), (void *)(PT_REGS_FP(ctx) + sizeof(ip))); }) + +#endif + +#ifndef PT_REGS_PARM1_SYSCALL +#define PT_REGS_PARM1_SYSCALL(x) PT_REGS_PARM1(x) +#endif +#define PT_REGS_PARM2_SYSCALL(x) PT_REGS_PARM2(x) +#define PT_REGS_PARM3_SYSCALL(x) PT_REGS_PARM3(x) +#ifndef PT_REGS_PARM4_SYSCALL +#define PT_REGS_PARM4_SYSCALL(x) PT_REGS_PARM4(x) +#endif +#define PT_REGS_PARM5_SYSCALL(x) PT_REGS_PARM5(x) + +#ifndef PT_REGS_PARM1_CORE_SYSCALL +#define PT_REGS_PARM1_CORE_SYSCALL(x) PT_REGS_PARM1_CORE(x) +#endif +#define PT_REGS_PARM2_CORE_SYSCALL(x) PT_REGS_PARM2_CORE(x) +#define PT_REGS_PARM3_CORE_SYSCALL(x) PT_REGS_PARM3_CORE(x) +#ifndef PT_REGS_PARM4_CORE_SYSCALL +#define PT_REGS_PARM4_CORE_SYSCALL(x) PT_REGS_PARM4_CORE(x) +#endif +#define PT_REGS_PARM5_CORE_SYSCALL(x) PT_REGS_PARM5_CORE(x) + +#else /* defined(bpf_target_defined) */ + +#define PT_REGS_PARM1(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define PT_REGS_PARM2(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define PT_REGS_PARM3(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define PT_REGS_PARM4(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define PT_REGS_PARM5(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define PT_REGS_RET(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define PT_REGS_FP(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define PT_REGS_RC(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define PT_REGS_SP(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define PT_REGS_IP(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) + +#define PT_REGS_PARM1_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define PT_REGS_PARM2_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define PT_REGS_PARM3_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define PT_REGS_PARM4_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define PT_REGS_PARM5_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define PT_REGS_RET_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define PT_REGS_FP_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define PT_REGS_RC_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define PT_REGS_SP_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define PT_REGS_IP_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) + +#define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define BPF_KRETPROBE_READ_RET_IP(ip, ctx) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) + +#define PT_REGS_PARM1_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define PT_REGS_PARM2_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define PT_REGS_PARM3_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define PT_REGS_PARM4_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define PT_REGS_PARM5_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) + +#define PT_REGS_PARM1_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define PT_REGS_PARM2_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define PT_REGS_PARM3_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define PT_REGS_PARM4_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) +#define PT_REGS_PARM5_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; }) + +#endif /* defined(bpf_target_defined) */ + +/* + * When invoked from a syscall handler kprobe, returns a pointer to a + * struct pt_regs containing syscall arguments and suitable for passing to + * PT_REGS_PARMn_SYSCALL() and PT_REGS_PARMn_CORE_SYSCALL(). + */ +#ifndef PT_REGS_SYSCALL_REGS +/* By default, assume that the arch selects ARCH_HAS_SYSCALL_WRAPPER. */ +#define PT_REGS_SYSCALL_REGS(ctx) ((struct pt_regs *)PT_REGS_PARM1(ctx)) +#endif + +#ifndef ___bpf_concat +#define ___bpf_concat(a, b) a ## b +#endif +#ifndef ___bpf_apply +#define ___bpf_apply(fn, n) ___bpf_concat(fn, n) +#endif +#ifndef ___bpf_nth +#define ___bpf_nth(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, _a, _b, _c, N, ...) N +#endif +#ifndef ___bpf_narg +#define ___bpf_narg(...) ___bpf_nth(_, ##__VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +#endif + +#define ___bpf_ctx_cast0() ctx +#define ___bpf_ctx_cast1(x) ___bpf_ctx_cast0(), (void *)ctx[0] +#define ___bpf_ctx_cast2(x, args...) ___bpf_ctx_cast1(args), (void *)ctx[1] +#define ___bpf_ctx_cast3(x, args...) ___bpf_ctx_cast2(args), (void *)ctx[2] +#define ___bpf_ctx_cast4(x, args...) ___bpf_ctx_cast3(args), (void *)ctx[3] +#define ___bpf_ctx_cast5(x, args...) ___bpf_ctx_cast4(args), (void *)ctx[4] +#define ___bpf_ctx_cast6(x, args...) ___bpf_ctx_cast5(args), (void *)ctx[5] +#define ___bpf_ctx_cast7(x, args...) ___bpf_ctx_cast6(args), (void *)ctx[6] +#define ___bpf_ctx_cast8(x, args...) ___bpf_ctx_cast7(args), (void *)ctx[7] +#define ___bpf_ctx_cast9(x, args...) ___bpf_ctx_cast8(args), (void *)ctx[8] +#define ___bpf_ctx_cast10(x, args...) ___bpf_ctx_cast9(args), (void *)ctx[9] +#define ___bpf_ctx_cast11(x, args...) ___bpf_ctx_cast10(args), (void *)ctx[10] +#define ___bpf_ctx_cast12(x, args...) ___bpf_ctx_cast11(args), (void *)ctx[11] +#define ___bpf_ctx_cast(args...) ___bpf_apply(___bpf_ctx_cast, ___bpf_narg(args))(args) + +/* + * BPF_PROG is a convenience wrapper for generic tp_btf/fentry/fexit and + * similar kinds of BPF programs, that accept input arguments as a single + * pointer to untyped u64 array, where each u64 can actually be a typed + * pointer or integer of different size. Instead of requring user to write + * manual casts and work with array elements by index, BPF_PROG macro + * allows user to declare a list of named and typed input arguments in the + * same syntax as for normal C function. All the casting is hidden and + * performed transparently, while user code can just assume working with + * function arguments of specified type and name. + * + * Original raw context argument is preserved as well as 'ctx' argument. + * This is useful when using BPF helpers that expect original context + * as one of the parameters (e.g., for bpf_perf_event_output()). + */ +#define BPF_PROG(name, args...) \ +name(unsigned long long *ctx); \ +static __attribute__((always_inline)) typeof(name(0)) \ +____##name(unsigned long long *ctx, ##args); \ +typeof(name(0)) name(unsigned long long *ctx) \ +{ \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ + return ____##name(___bpf_ctx_cast(args)); \ + _Pragma("GCC diagnostic pop") \ +} \ +static __attribute__((always_inline)) typeof(name(0)) \ +____##name(unsigned long long *ctx, ##args) + +struct pt_regs; + +#define ___bpf_kprobe_args0() ctx +#define ___bpf_kprobe_args1(x) ___bpf_kprobe_args0(), (void *)PT_REGS_PARM1(ctx) +#define ___bpf_kprobe_args2(x, args...) ___bpf_kprobe_args1(args), (void *)PT_REGS_PARM2(ctx) +#define ___bpf_kprobe_args3(x, args...) ___bpf_kprobe_args2(args), (void *)PT_REGS_PARM3(ctx) +#define ___bpf_kprobe_args4(x, args...) ___bpf_kprobe_args3(args), (void *)PT_REGS_PARM4(ctx) +#define ___bpf_kprobe_args5(x, args...) ___bpf_kprobe_args4(args), (void *)PT_REGS_PARM5(ctx) +#define ___bpf_kprobe_args(args...) ___bpf_apply(___bpf_kprobe_args, ___bpf_narg(args))(args) + +/* + * BPF_KPROBE serves the same purpose for kprobes as BPF_PROG for + * tp_btf/fentry/fexit BPF programs. It hides the underlying platform-specific + * low-level way of getting kprobe input arguments from struct pt_regs, and + * provides a familiar typed and named function arguments syntax and + * semantics of accessing kprobe input paremeters. + * + * Original struct pt_regs* context is preserved as 'ctx' argument. This might + * be necessary when using BPF helpers like bpf_perf_event_output(). + */ +#define BPF_KPROBE(name, args...) \ +name(struct pt_regs *ctx); \ +static __attribute__((always_inline)) typeof(name(0)) \ +____##name(struct pt_regs *ctx, ##args); \ +typeof(name(0)) name(struct pt_regs *ctx) \ +{ \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ + return ____##name(___bpf_kprobe_args(args)); \ + _Pragma("GCC diagnostic pop") \ +} \ +static __attribute__((always_inline)) typeof(name(0)) \ +____##name(struct pt_regs *ctx, ##args) + +#define ___bpf_kretprobe_args0() ctx +#define ___bpf_kretprobe_args1(x) ___bpf_kretprobe_args0(), (void *)PT_REGS_RC(ctx) +#define ___bpf_kretprobe_args(args...) ___bpf_apply(___bpf_kretprobe_args, ___bpf_narg(args))(args) + +/* + * BPF_KRETPROBE is similar to BPF_KPROBE, except, it only provides optional + * return value (in addition to `struct pt_regs *ctx`), but no input + * arguments, because they will be clobbered by the time probed function + * returns. + */ +#define BPF_KRETPROBE(name, args...) \ +name(struct pt_regs *ctx); \ +static __attribute__((always_inline)) typeof(name(0)) \ +____##name(struct pt_regs *ctx, ##args); \ +typeof(name(0)) name(struct pt_regs *ctx) \ +{ \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ + return ____##name(___bpf_kretprobe_args(args)); \ + _Pragma("GCC diagnostic pop") \ +} \ +static __always_inline typeof(name(0)) ____##name(struct pt_regs *ctx, ##args) + +/* If kernel has CONFIG_ARCH_HAS_SYSCALL_WRAPPER, read pt_regs directly */ +#define ___bpf_syscall_args0() ctx +#define ___bpf_syscall_args1(x) ___bpf_syscall_args0(), (void *)PT_REGS_PARM1_SYSCALL(regs) +#define ___bpf_syscall_args2(x, args...) ___bpf_syscall_args1(args), (void *)PT_REGS_PARM2_SYSCALL(regs) +#define ___bpf_syscall_args3(x, args...) ___bpf_syscall_args2(args), (void *)PT_REGS_PARM3_SYSCALL(regs) +#define ___bpf_syscall_args4(x, args...) ___bpf_syscall_args3(args), (void *)PT_REGS_PARM4_SYSCALL(regs) +#define ___bpf_syscall_args5(x, args...) ___bpf_syscall_args4(args), (void *)PT_REGS_PARM5_SYSCALL(regs) +#define ___bpf_syscall_args(args...) ___bpf_apply(___bpf_syscall_args, ___bpf_narg(args))(args) + +/* If kernel doesn't have CONFIG_ARCH_HAS_SYSCALL_WRAPPER, we have to BPF_CORE_READ from pt_regs */ +#define ___bpf_syswrap_args0() ctx +#define ___bpf_syswrap_args1(x) ___bpf_syswrap_args0(), (void *)PT_REGS_PARM1_CORE_SYSCALL(regs) +#define ___bpf_syswrap_args2(x, args...) ___bpf_syswrap_args1(args), (void *)PT_REGS_PARM2_CORE_SYSCALL(regs) +#define ___bpf_syswrap_args3(x, args...) ___bpf_syswrap_args2(args), (void *)PT_REGS_PARM3_CORE_SYSCALL(regs) +#define ___bpf_syswrap_args4(x, args...) ___bpf_syswrap_args3(args), (void *)PT_REGS_PARM4_CORE_SYSCALL(regs) +#define ___bpf_syswrap_args5(x, args...) ___bpf_syswrap_args4(args), (void *)PT_REGS_PARM5_CORE_SYSCALL(regs) +#define ___bpf_syswrap_args(args...) ___bpf_apply(___bpf_syswrap_args, ___bpf_narg(args))(args) + +/* + * BPF_KSYSCALL is a variant of BPF_KPROBE, which is intended for + * tracing syscall functions, like __x64_sys_close. It hides the underlying + * platform-specific low-level way of getting syscall input arguments from + * struct pt_regs, and provides a familiar typed and named function arguments + * syntax and semantics of accessing syscall input parameters. + * + * Original struct pt_regs * context is preserved as 'ctx' argument. This might + * be necessary when using BPF helpers like bpf_perf_event_output(). + * + * At the moment BPF_KSYSCALL does not transparently handle all the calling + * convention quirks for the following syscalls: + * + * - mmap(): __ARCH_WANT_SYS_OLD_MMAP. + * - clone(): CONFIG_CLONE_BACKWARDS, CONFIG_CLONE_BACKWARDS2 and + * CONFIG_CLONE_BACKWARDS3. + * - socket-related syscalls: __ARCH_WANT_SYS_SOCKETCALL. + * - compat syscalls. + * + * This may or may not change in the future. User needs to take extra measures + * to handle such quirks explicitly, if necessary. + * + * This macro relies on BPF CO-RE support and virtual __kconfig externs. + */ +#define BPF_KSYSCALL(name, args...) \ +name(struct pt_regs *ctx); \ +extern _Bool LINUX_HAS_SYSCALL_WRAPPER __kconfig; \ +static __attribute__((always_inline)) typeof(name(0)) \ +____##name(struct pt_regs *ctx, ##args); \ +typeof(name(0)) name(struct pt_regs *ctx) \ +{ \ + struct pt_regs *regs = LINUX_HAS_SYSCALL_WRAPPER \ + ? (struct pt_regs *)PT_REGS_PARM1(ctx) \ + : ctx; \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \ + if (LINUX_HAS_SYSCALL_WRAPPER) \ + return ____##name(___bpf_syswrap_args(args)); \ + else \ + return ____##name(___bpf_syscall_args(args)); \ + _Pragma("GCC diagnostic pop") \ +} \ +static __attribute__((always_inline)) typeof(name(0)) \ +____##name(struct pt_regs *ctx, ##args) + +#define BPF_KPROBE_SYSCALL BPF_KSYSCALL + +#endif diff --git a/ebpf_prog/common.h b/ebpf_prog/common.h new file mode 100644 index 0000000..70d3dea --- /dev/null +++ b/ebpf_prog/common.h @@ -0,0 +1,97 @@ +#ifndef OPENSNITCH_COMMON_H +#define OPENSNITCH_COMMON_H + +#include "common_defs.h" + +//https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/limits.h#L13 +#ifndef MAX_PATH_LEN + #define MAX_PATH_LEN 4096 +#endif + +//https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/binfmts.h#L16 +#define MAX_CMDLINE_LEN 4096 +// max args that I've been able to use before hitting the error: +// "dereference of modified ctx ptr disallowed" +#define MAX_ARGS 20 +#define MAX_ARG_SIZE 256 + +// flags to indicate if we were able to read all the cmdline arguments, +// or if one of the arguments is >= MAX_ARG_SIZE, or there more than MAX_ARGS +#define COMPLETE_ARGS 0 +#define INCOMPLETE_ARGS 1 + +#ifndef TASK_COMM_LEN + #define TASK_COMM_LEN 16 +#endif + +#define BUF_SIZE_MAP_NS 256 +#define GLOBAL_MAP_NS "256" +enum events_type { + EVENT_NONE = 0, + EVENT_EXEC, + EVENT_EXECVEAT, + EVENT_FORK, + EVENT_SCHED_EXIT, +}; + +struct trace_ev_common { + short common_type; + char common_flags; + char common_preempt_count; + int common_pid; +}; + +struct trace_sys_enter_execve { + struct trace_ev_common ext; + + int __syscall_nr; + char *filename; + const char *const *argv; + const char *const *envp; +}; + +struct trace_sys_enter_execveat { + struct trace_ev_common ext; + + int __syscall_nr; + char *filename; + const char *const *argv; + const char *const *envp; + int flags; +}; + +struct trace_sys_exit_execve { + struct trace_ev_common ext; + + int __syscall_nr; + long ret; +}; + + +struct data_t { + u64 type; + u32 pid; // PID as in the userspace term (i.e. task->tgid in kernel) + u32 uid; + // Parent PID as in the userspace term (i.e task->real_parent->tgid in kernel) + u32 ppid; + u32 ret_code; + u8 args_count; + u8 args_partial; + char filename[MAX_PATH_LEN]; + char args[MAX_ARGS][MAX_ARG_SIZE]; + char comm[TASK_COMM_LEN]; + u16 pad1; + u32 pad2; +}; + +//----------------------------------------------------------------------------- +// maps + +struct bpf_map_def SEC("maps/heapstore") heapstore = { + .type = BPF_MAP_TYPE_PERCPU_ARRAY, + .key_size = sizeof(u32), + .value_size = sizeof(struct data_t), + .max_entries = 1 +}; + +#endif diff --git a/ebpf_prog/common_defs.h b/ebpf_prog/common_defs.h new file mode 100644 index 0000000..da3c0d1 --- /dev/null +++ b/ebpf_prog/common_defs.h @@ -0,0 +1,40 @@ +#ifndef OPENSNITCH_COMMON_DEFS_H +#define OPENSNITCH_COMMON_DEFS_H + +#include <linux/sched.h> +#include <linux/ptrace.h> +#include <uapi/linux/bpf.h> +#include "bpf_headers/bpf_helpers.h" +#include "bpf_headers/bpf_tracing.h" +//#include <bpf/bpf_core_read.h> + +#define BUF_SIZE_MAP_NS 256 +#define MAPSIZE 12000 + +// even though we only need 32 bits of pid, on x86_32 ebpf verifier complained when pid type was set to u32 +typedef u64 pid_size_t; +typedef u64 uid_size_t; + + +//-------------------------------map definitions +// which github.com/iovisor/gobpf/elf expects +typedef struct bpf_map_def { + unsigned int type; + unsigned int key_size; + unsigned int value_size; + unsigned int max_entries; + unsigned int map_flags; + unsigned int pinning; + char namespace[BUF_SIZE_MAP_NS]; +} bpf_map_def; + +enum bpf_pin_type { + PIN_NONE = 0, + PIN_OBJECT_NS, + PIN_GLOBAL_NS, + PIN_CUSTOM_NS, +}; +//----------------------------------- + +#endif + diff --git a/ebpf_prog/opensnitch-dns.c b/ebpf_prog/opensnitch-dns.c new file mode 100644 index 0000000..7bb01b7 --- /dev/null +++ b/ebpf_prog/opensnitch-dns.c @@ -0,0 +1,235 @@ +/* Copyright (C) 2022 calesanz +// 2023-2024 Gustavo Iñiguez Goya +// +// This file is part of OpenSnitch. +// +// OpenSnitch is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// OpenSnitch is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with OpenSnitch. If not, see <http://www.gnu.org/licenses/>. +*/ + + +#define KBUILD_MODNAME "opensnitch-dns" + +#include <linux/in.h> +#include <linux/in6.h> +#include <linux/ptrace.h> +#include <linux/sched.h> +#include <net/sock.h> +#include <uapi/linux/bpf.h> +#include <uapi/linux/tcp.h> +#include "common_defs.h" +#include "bpf_headers/bpf_helpers.h" +#include "bpf_headers/bpf_tracing.h" + +//----------------------------------- + +// random values +#define MAX_ALIASES 5 +#define MAX_IPS 30 + +struct nameLookupEvent { + u32 addr_type; + u8 ip[16]; + char host[252]; +} __attribute__((packed)); + +struct hostent { + char *h_name; /* Official name of host. */ + char **h_aliases; /* Alias list. */ + int h_addrtype; /* Host address type. */ + int h_length; /* Length of address. */ + char **h_addr_list; /* List of addresses from name server. */ +#ifdef __USE_MISC +#define h_addr h_addr_list[0] /* Address, for backward compatibility.*/ +#endif +}; + +struct addrinfo { + int ai_flags; /* Input flags. */ + int ai_family; /* Protocol family for socket. */ + int ai_socktype; /* Socket type. */ + int ai_protocol; /* Protocol for socket. */ + size_t ai_addrlen; /* Length of socket address. */ + struct sockaddr *ai_addr; /* Socket address for socket. */ + char *ai_canonname; /* Canonical name for service location. */ + struct addrinfo *ai_next; /* Pointer to next in list. */ +}; + +struct addrinfo_args_cache { + struct addrinfo **addrinfo_ptr; + char node[256]; +}; +// define temporary array for data +struct bpf_map_def SEC("maps/addrinfo_args_hash") addrinfo_args_hash = { + .type = BPF_MAP_TYPE_HASH, + .max_entries = 256, // max entries at any time + .key_size = sizeof(u32), + .value_size = sizeof(struct addrinfo_args_cache), +}; + +// BPF output events +struct bpf_map_def SEC("maps/events") events = { + .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY, + .key_size = sizeof(u32), + .value_size = sizeof(u32), + .max_entries = 256, // max cpus +}; + +/** + * Hooks gethostbyname calls and emits multiple nameLookupEvent events. + * It supports at most MAX_IPS many addresses. + */ +SEC("uretprobe/gethostbyname") +int uretprobe__gethostbyname(struct pt_regs *ctx) { + // bpf_tracing_prinkt("Called gethostbyname %d\n",1); + struct nameLookupEvent data = {0}; + + if (!PT_REGS_RC(ctx)) + return 0; + + struct hostent *host = (struct hostent *)PT_REGS_RC(ctx); + char * hostnameptr = {0}; + bpf_probe_read(&hostnameptr, sizeof(hostnameptr), &host->h_name); + bpf_probe_read_str(&data.host, sizeof(data.host), hostnameptr); + + char **ips = {0}; + bpf_probe_read(&ips, sizeof(ips), &host->h_addr_list); + +#pragma clang loop unroll(full) + for (int i = 0; i < MAX_IPS; i++) { + char *ip={0}; + bpf_probe_read(&ip, sizeof(ip), &ips[i]); + + if (ip == NULL) { + return 0; + } + bpf_probe_read_user(&data.addr_type, sizeof(data.addr_type), + &host->h_addrtype); + + if (data.addr_type == AF_INET) { + // Only copy the 4 relevant bytes + bpf_probe_read_user(&data.ip, 4, ip); + } else { + bpf_probe_read_user(&data.ip, sizeof(data.ip), ip); + } + + bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &data, + sizeof(data)); + + // char **alias = host->h_aliases; + char **aliases = {0}; + bpf_probe_read(&aliases, sizeof(aliases), &host->h_aliases); + +#pragma clang loop unroll(full) + for (int j = 0; j < MAX_ALIASES; j++) { + char *alias = {0}; + bpf_probe_read(&alias, sizeof(alias), &aliases[j]); + + if (alias == NULL) { + return 0; + } + bpf_probe_read_user(&data.host, sizeof(data.host), alias); + bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &data, + sizeof(data)); + } + } + + return 0; +} + +// capture getaddrinfo call and store the relevant arguments to a hash. +SEC("uprobe/getaddrinfo") +int addrinfo(struct pt_regs *ctx) { + struct addrinfo_args_cache addrinfo_args = {0}; + if (!PT_REGS_PARM1(ctx)) + return 0; + if (!PT_REGS_PARM4(ctx)) + return 0; + + u64 pid_tgid = bpf_get_current_pid_tgid(); + u32 tid = (u32)pid_tgid; + + addrinfo_args.addrinfo_ptr = (struct addrinfo **)PT_REGS_PARM4(ctx); + + bpf_probe_read_user_str(&addrinfo_args.node, sizeof(addrinfo_args.node), + (char *)PT_REGS_PARM1(ctx)); + + bpf_map_update_elem(&addrinfo_args_hash, &tid, &addrinfo_args, + 0 /* flags */); + + return 0; +} + +SEC("uretprobe/getaddrinfo") +int ret_addrinfo(struct pt_regs *ctx) { + struct nameLookupEvent data = {0}; + struct addrinfo_args_cache *addrinfo_args = {0}; + + u64 pid_tgid = bpf_get_current_pid_tgid(); + u32 tid = (u32)pid_tgid; + + addrinfo_args = bpf_map_lookup_elem(&addrinfo_args_hash, &tid); + + if (addrinfo_args == 0) { + return 0; // missed start + } + + struct addrinfo **res_p={0}; + bpf_probe_read(&res_p, sizeof(res_p), &addrinfo_args->addrinfo_ptr); + +#pragma clang loop unroll(full) + for (int i = 0; i < MAX_IPS; i++) { + struct addrinfo *res={0}; + bpf_probe_read(&res, sizeof(res), res_p); + if (res == NULL) { + goto out; + } + bpf_probe_read(&data.addr_type, sizeof(data.addr_type), + &res->ai_family); + + if (data.addr_type == AF_INET) { + struct sockaddr_in *ipv4={0}; + bpf_probe_read(&ipv4, sizeof(ipv4), &res->ai_addr); + // Only copy the 4 relevant bytes + bpf_probe_read_user(&data.ip, 4, &ipv4->sin_addr); + } else if(data.addr_type == AF_INET6) { + struct sockaddr_in6 *ipv6={0}; + bpf_probe_read(&ipv6, sizeof(ipv6), &res->ai_addr); + + bpf_probe_read_user(&data.ip, sizeof(data.ip), &ipv6->sin6_addr); + } else { + goto out; + } + + bpf_probe_read_kernel_str(&data.host, sizeof(data.host), + &addrinfo_args->node); + + bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &data, + sizeof(data)); + + struct addrinfo * next={0}; + bpf_probe_read(&next, sizeof(next), &res->ai_next); + if (next == NULL){ + goto out; + } + res_p = &next; + } + +out: + bpf_map_delete_elem(&addrinfo_args_hash, &tid); + + return 0; +} + +char _license[] SEC("license") = "GPL"; +u32 _version SEC("version") = 0xFFFFFFFE; diff --git a/ebpf_prog/opensnitch-procs.c b/ebpf_prog/opensnitch-procs.c new file mode 100644 index 0000000..8603618 --- /dev/null +++ b/ebpf_prog/opensnitch-procs.c @@ -0,0 +1,205 @@ +#define KBUILD_MODNAME "opensnitch-procs" + +#include "common.h" + +struct bpf_map_def SEC("maps/proc-events") events = { + // Since kernel 4.4 + .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY, + .key_size = sizeof(u32), + .value_size = sizeof(u32), + .max_entries = 256, // max cpus +}; + +struct bpf_map_def SEC("maps/execMap") execMap = { + .type = BPF_MAP_TYPE_HASH, + .key_size = sizeof(u32), + .value_size = sizeof(struct data_t), + .max_entries = 256, +}; + + +static __always_inline void new_event(struct data_t* data) +{ + // initializing variables with __builtin_memset() is required + // for compatibility with bpf on kernel 4.4 + + struct task_struct *task; + struct task_struct *parent; + __builtin_memset(&task, 0, sizeof(task)); + __builtin_memset(&parent, 0, sizeof(parent)); + task = (struct task_struct *)bpf_get_current_task(); + bpf_probe_read(&parent, sizeof(parent), &task->real_parent); + data->pid = bpf_get_current_pid_tgid() >> 32; + +#if !defined(__arm__) && !defined(__i386__) + // on i686 -> invalid read from stack + bpf_probe_read(&data->ppid, sizeof(u32), &parent->tgid); +#endif + data->uid = bpf_get_current_uid_gid() & 0xffffffff; + bpf_get_current_comm(&data->comm, sizeof(data->comm)); +}; + +/* + * send to userspace the result of the execve* call. + */ +static __always_inline void __handle_exit_execve(struct trace_sys_exit_execve *ctx) +{ + u64 pid_tgid = bpf_get_current_pid_tgid(); + struct data_t *proc = bpf_map_lookup_elem(&execMap, &pid_tgid); + // don't delete the pid from execMap here, delegate it to sched_process_exit + if (proc == NULL) { return; } + if (ctx->ret != 0) { return; } + proc->ret_code = ctx->ret; + + bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, proc, sizeof(*proc)); +} + +// https://0xax.gitbooks.io/linux-insides/content/SysCall/linux-syscall-4.html +// bprm_execve REGS_PARM3 +// https://elixir.bootlin.com/linux/latest/source/fs/exec.c#L1796 + +SEC("tracepoint/sched/sched_process_exit") +int tracepoint__sched_sched_process_exit(struct pt_regs *ctx) +{ + u64 pid_tgid = bpf_get_current_pid_tgid(); + struct data_t *proc = bpf_map_lookup_elem(&execMap, &pid_tgid); + // if the pid is not in execMap cache (because it's not of a pid we've + // previously intercepted), do not send the event to userspace, because + // we won't do anything with it and it consumes CPU cycles (too much in some + // scenarios). + if (proc == NULL) { return 0; } + + int zero = 0; + struct data_t *data = bpf_map_lookup_elem(&heapstore, &zero); + if (!data){ return 0; } + + new_event(data); + data->type = EVENT_SCHED_EXIT; + bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, data, sizeof(*data)); + + bpf_map_delete_elem(&execMap, &pid_tgid); + return 0; +}; + +SEC("tracepoint/syscalls/sys_exit_execve") +int tracepoint__syscalls_sys_exit_execve(struct trace_sys_exit_execve *ctx) +{ + __handle_exit_execve(ctx); + return 0; +}; + +SEC("tracepoint/syscalls/sys_exit_execveat") +int tracepoint__syscalls_sys_exit_execveat(struct trace_sys_exit_execve *ctx) +{ + __handle_exit_execve(ctx); + return 0; +}; + +SEC("tracepoint/syscalls/sys_enter_execve") +int tracepoint__syscalls_sys_enter_execve(struct trace_sys_enter_execve* ctx) +{ + int zero = 0; + struct data_t *data = {0}; + data = (struct data_t *)bpf_map_lookup_elem(&heapstore, &zero); + if (!data){ return 0; } + + new_event(data); + data->type = EVENT_EXEC; + // bpf_probe_read_user* helpers were introduced in kernel 5.5 + // Since the args can be overwritten anyway, maybe we could get them from + // mm_struct instead for a wider kernel version support range? + bpf_probe_read_user_str(&data->filename, sizeof(data->filename), (const char *)ctx->filename); + + const char *argp={0}; + data->args_count = 0; + data->args_partial = INCOMPLETE_ARGS; + +// FIXME: on i386 arch, the following code fails with permission denied. +#if !defined(__arm__) && !defined(__i386__) + #pragma unroll + for (int i = 0; i < MAX_ARGS; i++) { + bpf_probe_read_user(&argp, sizeof(argp), &ctx->argv[i]); + if (!argp){ data->args_partial = COMPLETE_ARGS; break; } + + if (bpf_probe_read_user_str(&data->args[i], MAX_ARG_SIZE, argp) >= MAX_ARG_SIZE){ + break; + } + data->args_count++; + } +#endif + +// FIXME: on aarch64 we fail to save the event to execMap, so send it to userspace here. +#if defined(__aarch64__) + bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, data, sizeof(*data)); +#else + // in case of failure adding the item to the map, send it directly + u64 pid_tgid = bpf_get_current_pid_tgid(); + if (bpf_map_update_elem(&execMap, &pid_tgid, data, BPF_ANY) != 0) { + + // With some commands, this helper fails with error -28 (ENOSPC). Misleading error? cmd failed maybe? + // BUG: after coming back from suspend state, this helper fails with error -95 (EOPNOTSUPP) + // Possible workaround: count -95 errors, and from userspace reinitialize the streamer if errors >= n-errors + bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, data, sizeof(*data)); + } +#endif + + return 0; +}; + +SEC("tracepoint/syscalls/sys_enter_execveat") +int tracepoint__syscalls_sys_enter_execveat(struct trace_sys_enter_execveat* ctx) +{ + int zero = 0; + struct data_t *data = {0}; + data = (struct data_t *)bpf_map_lookup_elem(&heapstore, &zero); + if (!data){ return 0; } + + new_event((void *)data); + data->type = EVENT_EXECVEAT; + // bpf_probe_read_user* helpers were introduced in kernel 5.5 + // Since the args can be overwritten anyway, maybe we could get them from + // mm_struct instead for a wider kernel version support range? + bpf_probe_read_user_str(&data->filename, sizeof(data->filename), (const char *)ctx->filename); + + const char *argp={0}; + data->args_count = 0; + data->args_partial = INCOMPLETE_ARGS; + +// FIXME: on i386 arch, the following code fails with permission denied. +#if !defined(__arm__) && !defined(__i386__) + #pragma unroll + for (int i = 0; i < MAX_ARGS; i++) { + bpf_probe_read_user(&argp, sizeof(argp), &ctx->argv[i]); + if (!argp){ data->args_partial = COMPLETE_ARGS; break; } + + if (bpf_probe_read_user_str(&data->args[i], MAX_ARG_SIZE, argp) >= MAX_ARG_SIZE){ + break; + } + data->args_count++; + } +#endif + +// FIXME: on aarch64 we fail to save the event to execMap, so send it to userspace here. +#if defined(__aarch64__) + bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, data, sizeof(*data)); +#else + // in case of failure adding the item to the map, send it directly + u64 pid_tgid = bpf_get_current_pid_tgid(); + if (bpf_map_update_elem(&execMap, &pid_tgid, data, BPF_ANY) != 0) { + + // With some commands, this helper fails with error -28 (ENOSPC). Misleading error? cmd failed maybe? + // BUG: after coming back from suspend state, this helper fails with error -95 (EOPNOTSUPP) + // Possible workaround: count -95 errors, and from userspace reinitialize the streamer if errors >= n-errors + bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, data, sizeof(*data)); + } +#endif + + return 0; +}; + + + +char _license[] SEC("license") = "GPL"; +// this number will be interpreted by the elf loader +// to set the current running kernel version +u32 _version SEC("version") = 0xFFFFFFFE; diff --git a/ebpf_prog/opensnitch.c b/ebpf_prog/opensnitch.c new file mode 100644 index 0000000..496619d --- /dev/null +++ b/ebpf_prog/opensnitch.c @@ -0,0 +1,529 @@ +#define KBUILD_MODNAME "dummy" + +#include "common_defs.h" +#include <uapi/linux/tcp.h> +#include <net/sock.h> +#include <net/udp_tunnel.h> +#include <net/inet_sock.h> + +struct tcp_key_t { + u16 sport; + u32 daddr; + u16 dport; + u32 saddr; +}__attribute__((packed)); + +struct tcp_value_t { + pid_size_t pid; + uid_size_t uid; + char comm[TASK_COMM_LEN]; +}__attribute__((packed)); + +// not using unsigned __int128 because it is not supported on x86_32 +struct ipV6 { + u64 part1; + u64 part2; +}__attribute__((packed)); + +struct tcpv6_key_t { + u16 sport; + struct ipV6 daddr; + u16 dport; + struct ipV6 saddr; +}__attribute__((packed)); + +struct tcpv6_value_t{ + pid_size_t pid; + uid_size_t uid; + char comm[TASK_COMM_LEN]; +}__attribute__((packed)); + +struct udp_key_t { + u16 sport; + u32 daddr; + u16 dport; + u32 saddr; +} __attribute__((packed)); + +struct udp_value_t{ + pid_size_t pid; + uid_size_t uid; + char comm[TASK_COMM_LEN]; +}__attribute__((packed)); + +struct udpv6_key_t { + u16 sport; + struct ipV6 daddr; + u16 dport; + struct ipV6 saddr; +}__attribute__((packed)); + +struct udpv6_value_t{ + pid_size_t pid; + uid_size_t uid; + char comm[TASK_COMM_LEN]; +}__attribute__((packed)); + + +// on x86_32 "struct sock" is arranged differently from x86_64 (at least on Debian kernels). +// We hardcode offsets of IP addresses. +struct sock_on_x86_32_t { + u8 data_we_dont_care_about[40]; + struct ipV6 daddr; + struct ipV6 saddr; +}; + + +// Add +1,+2,+3 etc. to map size helps to easier distinguish maps in bpftool's output +struct bpf_map_def SEC("maps/tcpMap") tcpMap = { + .type = BPF_MAP_TYPE_HASH, + .key_size = sizeof(struct tcp_key_t), + .value_size = sizeof(struct tcp_value_t), + .max_entries = MAPSIZE+1, +}; +struct bpf_map_def SEC("maps/tcpv6Map") tcpv6Map = { + .type = BPF_MAP_TYPE_HASH, + .key_size = sizeof(struct tcpv6_key_t), + .value_size = sizeof(struct tcpv6_value_t), + .max_entries = MAPSIZE+2, +}; +struct bpf_map_def SEC("maps/udpMap") udpMap = { + .type = BPF_MAP_TYPE_HASH, + .key_size = sizeof(struct udp_key_t), + .value_size = sizeof(struct udp_value_t), + .max_entries = MAPSIZE+3, +}; +struct bpf_map_def SEC("maps/udpv6Map") udpv6Map = { + .type = BPF_MAP_TYPE_HASH, + .key_size = sizeof(struct udpv6_key_t), + .value_size = sizeof(struct udpv6_value_t), + .max_entries = MAPSIZE+4, +}; + +// for TCP the IP-tuple can be copied from "struct sock" only upon return from tcp_connect(). +// We stash the socket here to look it up upon return. +struct bpf_map_def SEC("maps/tcpsock") tcpsock = { + .type = BPF_MAP_TYPE_HASH, + .key_size = sizeof(u64), + // using u64 instead of sizeof(struct sock *) + // to avoid pointer size related quirks on x86_32 + .value_size = sizeof(u64), + .max_entries = 300, +}; +struct bpf_map_def SEC("maps/tcpv6sock") tcpv6sock = { + .type = BPF_MAP_TYPE_HASH, + .key_size = sizeof(u64), + .value_size = sizeof(u64), + .max_entries = 300, +}; +struct bpf_map_def SEC("maps/icmpsock") icmpsock = { + .type = BPF_MAP_TYPE_HASH, + .key_size = sizeof(u64), + .value_size = sizeof(u64), + .max_entries = 300, +}; + + +// initializing variables with __builtin_memset() is required +// for compatibility with bpf on kernel 4.4 + +SEC("kprobe/tcp_v4_connect") +int kprobe__tcp_v4_connect(struct pt_regs *ctx) +{ +#if defined(__i386__) + // On x86_32 platforms I couldn't get function arguments using PT_REGS_PARM1 + // that's why we are accessing registers directly + struct sock *sk = (struct sock *)((ctx)->ax); +#else + struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx); +#endif + + u64 skp = (u64)sk; + u64 pid_tgid = bpf_get_current_pid_tgid(); + bpf_map_update_elem(&tcpsock, &pid_tgid, &skp, BPF_ANY); + return 0; +}; + +SEC("kretprobe/tcp_v4_connect") +int kretprobe__tcp_v4_connect(struct pt_regs *ctx) +{ + u64 pid_tgid = bpf_get_current_pid_tgid(); + u64 *skp = bpf_map_lookup_elem(&tcpsock, &pid_tgid); + if (skp == NULL) {return 0;} + + struct sock *sk; + __builtin_memset(&sk, 0, sizeof(sk)); + sk = (struct sock *)*skp; + + struct tcp_key_t tcp_key; + __builtin_memset(&tcp_key, 0, sizeof(tcp_key)); + bpf_probe_read(&tcp_key.dport, sizeof(tcp_key.dport), &sk->__sk_common.skc_dport); + bpf_probe_read(&tcp_key.sport, sizeof(tcp_key.sport), &sk->__sk_common.skc_num); + bpf_probe_read(&tcp_key.daddr, sizeof(tcp_key.daddr), &sk->__sk_common.skc_daddr); + bpf_probe_read(&tcp_key.saddr, sizeof(tcp_key.saddr), &sk->__sk_common.skc_rcv_saddr); + + struct tcp_value_t tcp_value={0}; + __builtin_memset(&tcp_value, 0, sizeof(tcp_value)); + tcp_value.pid = pid_tgid >> 32; + tcp_value.uid = bpf_get_current_uid_gid() & 0xffffffff; + bpf_get_current_comm(&tcp_value.comm, sizeof(tcp_value.comm)); + bpf_map_update_elem(&tcpMap, &tcp_key, &tcp_value, BPF_ANY); + + bpf_map_delete_elem(&tcpsock, &pid_tgid); + return 0; +}; + +SEC("kprobe/tcp_v6_connect") +int kprobe__tcp_v6_connect(struct pt_regs *ctx) +{ +#if defined(__i386__) + struct sock *sk = (struct sock *)((ctx)->ax); +#else + struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx); +#endif + + u64 skp = (u64)sk; + u64 pid_tgid = bpf_get_current_pid_tgid(); + bpf_map_update_elem(&tcpv6sock, &pid_tgid, &skp, BPF_ANY); + return 0; +}; + +SEC("kretprobe/tcp_v6_connect") +int kretprobe__tcp_v6_connect(struct pt_regs *ctx) +{ + u64 pid_tgid = bpf_get_current_pid_tgid(); + u64 *skp = bpf_map_lookup_elem(&tcpv6sock, &pid_tgid); + if (skp == NULL) {return 0;} + + struct sock *sk; + __builtin_memset(&sk, 0, sizeof(sk)); + sk = (struct sock *)*skp; + + struct tcpv6_key_t tcpv6_key; + __builtin_memset(&tcpv6_key, 0, sizeof(tcpv6_key)); + bpf_probe_read(&tcpv6_key.dport, sizeof(tcpv6_key.dport), &sk->__sk_common.skc_dport); + bpf_probe_read(&tcpv6_key.sport, sizeof(tcpv6_key.sport), &sk->__sk_common.skc_num); +#if defined(__i386__) + struct sock_on_x86_32_t sock; + __builtin_memset(&sock, 0, sizeof(sock)); + bpf_probe_read(&sock, sizeof(sock), *(&sk)); + tcpv6_key.daddr = sock.daddr; + tcpv6_key.saddr = sock.saddr; +#else + bpf_probe_read(&tcpv6_key.daddr, sizeof(tcpv6_key.daddr), &sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32); + bpf_probe_read(&tcpv6_key.saddr, sizeof(tcpv6_key.saddr), &sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32); +#endif + + struct tcpv6_value_t tcpv6_value={0}; + __builtin_memset(&tcpv6_value, 0, sizeof(tcpv6_value)); + tcpv6_value.pid = pid_tgid >> 32; + tcpv6_value.uid = bpf_get_current_uid_gid() & 0xffffffff; + bpf_get_current_comm(&tcpv6_value.comm, sizeof(tcpv6_value.comm)); + bpf_map_update_elem(&tcpv6Map, &tcpv6_key, &tcpv6_value, BPF_ANY); + + bpf_map_delete_elem(&tcpv6sock, &pid_tgid); + return 0; +}; + +SEC("kprobe/udp_sendmsg") +int kprobe__udp_sendmsg(struct pt_regs *ctx) +{ +#if defined(__i386__) + struct sock *sk = (struct sock *)((ctx)->ax); + struct msghdr *msg = (struct msghdr *)((ctx)->dx); +#else + struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx); + struct msghdr *msg = (struct msghdr *)PT_REGS_PARM2(ctx); +#endif + + u64 msg_name; //pointer + __builtin_memset(&msg_name, 0, sizeof(msg_name)); + bpf_probe_read(&msg_name, sizeof(msg_name), &msg->msg_name); + struct sockaddr_in * usin = (struct sockaddr_in *)msg_name; + + struct udp_key_t udp_key; + __builtin_memset(&udp_key, 0, sizeof(udp_key)); + bpf_probe_read(&udp_key.dport, sizeof(udp_key.dport), &usin->sin_port); + if (udp_key.dport != 0){ //likely + bpf_probe_read(&udp_key.daddr, sizeof(udp_key.daddr), &usin->sin_addr.s_addr); + } + else { + //very rarely dport can be found in skc_dport + bpf_probe_read(&udp_key.dport, sizeof(udp_key.dport), &sk->__sk_common.skc_dport); + bpf_probe_read(&udp_key.daddr, sizeof(udp_key.daddr), &sk->__sk_common.skc_daddr); + } + bpf_probe_read(&udp_key.sport, sizeof(udp_key.sport), &sk->__sk_common.skc_num); + bpf_probe_read(&udp_key.saddr, sizeof(udp_key.saddr), &sk->__sk_common.skc_rcv_saddr); + +// TODO: armhf +#if !defined(__arm__) + // extract from the ancillary message the source IP. + if (udp_key.saddr == 0){ + u64 cmsg=0; + bpf_probe_read(&cmsg, sizeof(cmsg), &msg->msg_control); + struct in_pktinfo *inpkt = (struct in_pktinfo *)CMSG_DATA(cmsg); + bpf_probe_read(&udp_key.saddr, sizeof(udp_key.saddr), &inpkt->ipi_spec_dst.s_addr); + } +#endif + + u32 zero_key = 0; + __builtin_memset(&zero_key, 0, sizeof(zero_key)); + struct udp_value_t *lookedupValue = bpf_map_lookup_elem(&udpMap, &udp_key); + u64 pid = bpf_get_current_pid_tgid() >> 32; + if (lookedupValue == NULL || lookedupValue->pid != pid) { + struct udp_value_t udp_value={0}; + __builtin_memset(&udp_value, 0, sizeof(udp_value)); + udp_value.pid = pid; + udp_value.uid = bpf_get_current_uid_gid() & 0xffffffff; + bpf_get_current_comm(&udp_value.comm, sizeof(udp_value.comm)); + bpf_map_update_elem(&udpMap, &udp_key, &udp_value, BPF_ANY); + } + //else nothing to do + return 0; +}; + + +SEC("kprobe/udpv6_sendmsg") +int kprobe__udpv6_sendmsg(struct pt_regs *ctx) +{ +#if defined(__i386__) + struct sock *sk = (struct sock *)((ctx)->ax); + struct msghdr *msg = (struct msghdr *)((ctx)->dx); +#else + struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx); + struct msghdr *msg = (struct msghdr *)PT_REGS_PARM2(ctx); +#endif + + u64 msg_name; //a pointer + __builtin_memset(&msg_name, 0, sizeof(msg_name)); + bpf_probe_read(&msg_name, sizeof(msg_name), &msg->msg_name); + + struct udpv6_key_t udpv6_key; + __builtin_memset(&udpv6_key, 0, sizeof(udpv6_key)); + bpf_probe_read(&udpv6_key.dport, sizeof(udpv6_key.dport), &sk->__sk_common.skc_dport); + if (udpv6_key.dport != 0){ //likely + bpf_probe_read(&udpv6_key.daddr, sizeof(udpv6_key.daddr), &sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32); + } + else { + struct sockaddr_in6 * sin6 = (struct sockaddr_in6 *)msg_name; + bpf_probe_read(&udpv6_key.dport, sizeof(udpv6_key.dport), &sin6->sin6_port); + bpf_probe_read(&udpv6_key.daddr, sizeof(udpv6_key.daddr), &sin6->sin6_addr.in6_u.u6_addr32); + } + + bpf_probe_read(&udpv6_key.sport, sizeof(udpv6_key.sport), &sk->__sk_common.skc_num); + bpf_probe_read(&udpv6_key.saddr, sizeof(udpv6_key.saddr), &sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32); + + if (udpv6_key.saddr.part1 == 0){ + u64 cmsg=0; + bpf_probe_read(&cmsg, sizeof(cmsg), &msg->msg_control); + struct in6_pktinfo *inpkt = (struct in6_pktinfo *)CMSG_DATA(cmsg); + bpf_probe_read(&udpv6_key.saddr, sizeof(udpv6_key.saddr), &inpkt->ipi6_addr.s6_addr32); + } + +#if defined(__i386__) + struct sock_on_x86_32_t sock; + __builtin_memset(&sock, 0, sizeof(sock)); + bpf_probe_read(&sock, sizeof(sock), *(&sk)); + udpv6_key.daddr = sock.daddr; + udpv6_key.saddr = sock.saddr; +#endif + + struct udpv6_value_t *lookedupValue = bpf_map_lookup_elem(&udpv6Map, &udpv6_key); + u64 pid = bpf_get_current_pid_tgid() >> 32; + if ( lookedupValue == NULL || lookedupValue->pid != pid) { + struct udpv6_value_t udpv6_value={0}; + __builtin_memset(&udpv6_value, 0, sizeof(udpv6_value)); + bpf_get_current_comm(&udpv6_value.comm, sizeof(udpv6_value.comm)); + udpv6_value.pid = pid; + udpv6_value.uid = bpf_get_current_uid_gid() & 0xffffffff; + bpf_map_update_elem(&udpv6Map, &udpv6_key, &udpv6_value, BPF_ANY); + } + //else nothing to do + return 0; +}; + +// TODO: armhf +#if !defined(__arm__) +SEC("kprobe/inet_dgram_connect") +int kprobe__inet_dgram_connect(struct pt_regs *ctx) +{ +#if defined(__i386__) + struct socket *skt = (struct socket *)PT_REGS_PARM1(ctx); + struct sockaddr *saddr = (struct sockaddr *)PT_REGS_PARM2(ctx); +#else + struct socket *skt = (struct socket *)PT_REGS_PARM1(ctx); + struct sockaddr *saddr = (struct sockaddr *)PT_REGS_PARM2(ctx); +#endif + + u64 pid_tgid = bpf_get_current_pid_tgid(); + u64 skp = (u64)skt; + u64 sa = (u64)saddr; + bpf_map_update_elem(&tcpsock, &pid_tgid, &skp, BPF_ANY); + bpf_map_update_elem(&icmpsock, &pid_tgid, &sa, BPF_ANY); + return 0; +} + +SEC("kretprobe/inet_dgram_connect") +int kretprobe__inet_dgram_connect(int retval) +{ + u64 pid_tgid = bpf_get_current_pid_tgid(); + u64 *skp = bpf_map_lookup_elem(&tcpsock, &pid_tgid); + if (skp == NULL) { goto out; } + u64 *sap = bpf_map_lookup_elem(&icmpsock, &pid_tgid); + if (sap == NULL) { goto out; } + + struct sock *sk; + struct socket *skt; + __builtin_memset(&sk, 0, sizeof(sk)); + __builtin_memset(&skt, 0, sizeof(skt)); + skt = (struct socket *)*skp; + bpf_probe_read(&sk, sizeof(sk), &skt->sk); + + u8 proto = 0; + u8 type = 0; + u8 fam = 0; + bpf_probe_read(&proto, sizeof(proto), &sk->sk_protocol); + bpf_probe_read(&type, sizeof(type), &sk->sk_type); + bpf_probe_read(&fam, sizeof(type), &sk->sk_family); + + struct udp_value_t udp_value={0}; + __builtin_memset(&udp_value, 0, sizeof(udp_value)); + udp_value.pid = pid_tgid >> 32; + udp_value.uid = bpf_get_current_uid_gid() & 0xffffffff; + bpf_get_current_comm(&udp_value.comm, sizeof(udp_value.comm)); + + if (fam == AF_INET){ + struct sockaddr_in *ska; + struct udp_key_t udp_key; + __builtin_memset(&ska, 0, sizeof(ska)); + __builtin_memset(&udp_key, 0, sizeof(udp_key)); + ska = (struct sockaddr_in *)*sap; + + bpf_probe_read(&udp_key.daddr, sizeof(udp_key.daddr), &ska->sin_addr.s_addr); + bpf_probe_read(&udp_key.dport, sizeof(udp_key.dport), &ska->sin_port); + if (udp_key.dport == 0){ + bpf_probe_read(&udp_key.dport, sizeof(udp_key.dport), &sk->__sk_common.skc_dport); + bpf_probe_read(&udp_key.daddr, sizeof(udp_key.daddr), &sk->__sk_common.skc_daddr); + } + bpf_probe_read(&udp_key.sport, sizeof(udp_key.sport), &sk->__sk_common.skc_num); + bpf_probe_read(&udp_key.saddr, sizeof(udp_key.saddr), &sk->__sk_common.skc_rcv_saddr); + + udp_key.sport = (udp_key.sport >> 8) | ((udp_key.sport << 8) & 0xff00); + + // There're several reasons for these fields to be empty: + // - saddr may be empty if sk_state is 7 (CLOSE) + // - <insert more here> + if (udp_key.dport == 0 || udp_key.daddr == 0){ + goto out; + } + + if (proto == IPPROTO_UDP){ + bpf_map_update_elem(&udpMap, &udp_key, &udp_value, BPF_ANY); + } + } else if (fam == AF_INET6){ + struct sockaddr_in6 *ska; + struct udpv6_key_t udpv6_key; + __builtin_memset(&ska, 0, sizeof(ska)); + __builtin_memset(&udpv6_key, 0, sizeof(udpv6_key)); + ska = (struct sockaddr_in6 *)*sap; + + bpf_probe_read(&udpv6_key.dport, sizeof(udpv6_key.dport), &sk->__sk_common.skc_dport); + if (udpv6_key.dport != 0){ //likely + bpf_probe_read(&udpv6_key.daddr, sizeof(udpv6_key.daddr), &sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32); + } + else { + bpf_probe_read(&udpv6_key.dport, sizeof(udpv6_key.dport), &ska->sin6_port); + bpf_probe_read(&udpv6_key.daddr, sizeof(udpv6_key.daddr), &ska->sin6_addr.in6_u.u6_addr32); + } + + bpf_probe_read(&udpv6_key.sport, sizeof(udpv6_key.sport), &sk->__sk_common.skc_num); + bpf_probe_read(&udpv6_key.saddr, sizeof(udpv6_key.saddr), &sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32); + +#if defined(__i386__) + struct sock_on_x86_32_t sock; + __builtin_memset(&sock, 0, sizeof(sock)); + bpf_probe_read(&sock, sizeof(sock), *(&sk)); + udpv6_key.daddr = sock.daddr; + udpv6_key.saddr = sock.saddr; +#endif + + if (udpv6_key.dport == 0){ + goto out; + } + + if (proto == IPPROTO_UDP){ + bpf_map_update_elem(&udpv6Map, &udpv6_key, &udp_value, BPF_ANY); + } + } + //if (proto == IPPROTO_UDP && type == SOCK_DGRAM && udp_key.dport == 1025){ + // udp_key.dport = 0; + // udp_key.sport = 0; + // bpf_map_update_elem(&icmpMap, &udp_key, &udp_value, BPF_ANY); + //} + //else if (proto == IPPROTO_UDP && type == SOCK_DGRAM && udp_key.dport != 1025){ + // bpf_map_update_elem(&icmpMap, &udp_key, &udp_value, BPF_ANY); + //} else if (proto == IPPROTO_TCP && type == SOCK_RAW){ + // sport always 6 and dport 0 + // bpf_map_update_elem(&tcpMap, &udp_key, &udp_value, BPF_ANY); + //} + + return 0; +out: + bpf_map_delete_elem(&tcpsock, &pid_tgid); + bpf_map_delete_elem(&icmpsock, &pid_tgid); + + return 0; +}; +#endif + +// TODO: for 32bits +#if !defined(__arm__) && !defined(__i386__) + +SEC("kprobe/iptunnel_xmit") +int kprobe__iptunnel_xmit(struct pt_regs *ctx) +{ + struct sk_buff *skb = (struct sk_buff *)PT_REGS_PARM3(ctx); + u32 src = (u32)PT_REGS_PARM4(ctx); + u32 dst = (u32)PT_REGS_PARM5(ctx); + + u16 sport = 0; + unsigned char *head; + u16 pkt_hdr; + __builtin_memset(&head, 0, sizeof(head)); + __builtin_memset(&pkt_hdr, 0, sizeof(pkt_hdr)); + bpf_probe_read(&head, sizeof(head), &skb->head); + bpf_probe_read(&pkt_hdr, sizeof(pkt_hdr), &skb->transport_header); + struct udphdr *udph; + __builtin_memset(&udph, 0, sizeof(udph)); + + udph = (struct udphdr *)(head + pkt_hdr); + bpf_probe_read(&sport, sizeof(sport), &udph->source); + sport = (sport >> 8) | ((sport << 8) & 0xff00); + + struct udp_key_t udp_key; + struct udp_value_t udp_value; + __builtin_memset(&udp_key, 0, sizeof(udp_key)); + __builtin_memset(&udp_value, 0, sizeof(udp_value)); + + bpf_probe_read(&udp_key.sport, sizeof(udp_key.sport), &sport); + bpf_probe_read(&udp_key.dport, sizeof(udp_key.dport), &udph->dest); + bpf_probe_read(&udp_key.saddr, sizeof(udp_key.saddr), &src); + bpf_probe_read(&udp_key.daddr, sizeof(udp_key.daddr), &dst); + + struct udp_value_t *lookedupValue = bpf_map_lookup_elem(&udpMap, &udp_key); + u64 pid = bpf_get_current_pid_tgid() >> 32; + if ( lookedupValue == NULL || lookedupValue->pid != pid) { + bpf_get_current_comm(&udp_value.comm, sizeof(udp_value.comm)); + udp_value.pid = pid; + udp_value.uid = bpf_get_current_uid_gid() & 0xffffffff; + bpf_map_update_elem(&udpMap, &udp_key, &udp_value, BPF_ANY); + } + + return 0; +}; +#endif + +char _license[] SEC("license") = "GPL"; +// this number will be interpreted by the elf loader +// to set the current running kernel version +u32 _version SEC("version") = 0xFFFFFFFE; diff --git a/proto/.gitignore b/proto/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/proto/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/proto/Makefile b/proto/Makefile new file mode 100644 index 0000000..a58241c --- /dev/null +++ b/proto/Makefile @@ -0,0 +1,13 @@ +all: ../daemon/ui/protocol/ui.pb.go ../ui/opensnitch/ui_pb2.py + +../daemon/ui/protocol/ui.pb.go: ui.proto + protoc -I. ui.proto --go_out=../daemon/ui/protocol/ --go-grpc_out=../daemon/ui/protocol/ --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative + +../ui/opensnitch/ui_pb2.py: ui.proto + python3 -m grpc_tools.protoc -I. --python_out=../ui/opensnitch/ --grpc_python_out=../ui/opensnitch/ ui.proto + +clean: + @rm -rf ../daemon/ui/protocol/ui.pb.go + @rm -rf ../daemon/ui/protocol/ui_grpc.pb.go + @rm -rf ../ui/opensnitch/ui_pb2.py + @rm -rf ../ui/opensnitch/ui_pb2_grpc.py diff --git a/proto/ui.proto b/proto/ui.proto new file mode 100644 index 0000000..2c5b3c0 --- /dev/null +++ b/proto/ui.proto @@ -0,0 +1,264 @@ +syntax = "proto3"; + +package protocol; + +option go_package = "github.com/evilsocket/opensnitch/daemon/ui/protocol"; + +service UI { + rpc Ping(PingRequest) returns (PingReply) {} + rpc AskRule (Connection) returns (Rule) {} + rpc Subscribe (ClientConfig) returns (ClientConfig) {} + rpc Notifications (stream NotificationReply) returns (stream Notification) {} + rpc PostAlert(Alert) returns (MsgResponse) {} +} + +/** + - Send error messages (kernel not compatible, etc) + - Send warnings (eBPF modules failed loading, etc) + - Send kernel events: new execs, bytes recv/sent, ... + - Alert of events defined by the user: alert when a rule matches +*/ +message Alert { + enum Priority { + LOW = 0; + MEDIUM = 1; + HIGH = 2; + } + enum Type { + ERROR = 0; + WARNING = 1; + INFO = 2; + } + enum Action { + NONE = 0; + SHOW_ALERT = 1; + SAVE_TO_DB = 2; + } + // What caused the alert + enum What { + GENERIC = 0; + PROC_MONITOR = 1; + FIREWALL = 2; + CONNECTION = 3; + RULE = 4; + NETLINK = 5; + // bind, exec, etc + KERNEL_EVENT = 6; + } + + uint64 id = 1; + Type type = 2; + // TODO: group of actions: SHOW_ALERT | SAVE_TO_DB + Action action = 3; + Priority priority = 4; + What what = 5; + // https://developers.google.com/protocol-buffers/docs/reference/go-generated#oneof + oneof data { + // errors, messages, etc + string text = 6; + // proc events: send/recv bytes, etc + Process proc = 8; + // conn events: bind, listen, etc + Connection conn = 9; + Rule rule = 10; + FwRule fwrule = 11; + } +} + +message MsgResponse { + uint64 id = 1; +} + +message Event { + string time = 1; + Connection connection = 2; + Rule rule = 3; + int64 unixnano = 4; +} + +message Statistics { + string daemon_version = 1; + uint64 rules = 2; + uint64 uptime = 3; + uint64 dns_responses = 4; + uint64 connections = 5; + uint64 ignored = 6; + uint64 accepted = 7; + uint64 dropped = 8; + uint64 rule_hits = 9; + uint64 rule_misses = 10; + map<string, uint64> by_proto = 11; + map<string, uint64> by_address = 12; + map<string, uint64> by_host = 13; + map<string, uint64> by_port = 14; + map<string, uint64> by_uid = 15; + map<string, uint64> by_executable = 16; + repeated Event events = 17; +} + +message PingRequest { + uint64 id = 1; + Statistics stats = 2; +} + +message PingReply { + uint64 id = 1; +} + +message Process { + uint64 pid = 1; + uint64 ppid = 2; + uint64 uid = 3; + string comm = 4; + string path = 5; + repeated string args = 6; + map<string, string> env = 7; + string cwd = 8; + uint64 io_reads = 9; + uint64 io_writes = 10; + uint64 net_reads = 11; + uint64 net_writes = 12; +} + +message Connection { + string protocol = 1; + string src_ip = 2; + uint32 src_port = 3; + string dst_ip = 4; + string dst_host = 5; + uint32 dst_port = 6; + uint32 user_id = 7; + uint32 process_id = 8; + string process_path = 9; + string process_cwd = 10; + repeated string process_args = 11; + map<string, string> process_env = 12; +} + +message Operator { + string type = 1; + string operand = 2; + string data = 3; + bool sensitive = 4; + repeated Operator list = 5; +} + +message Rule { + int64 created = 1; + string name = 2; + string description = 3; + bool enabled = 4; + bool precedence = 5; + bool nolog = 6; + string action = 7; + string duration = 8; + Operator operator = 9; +} + +enum Action { + NONE = 0; + ENABLE_INTERCEPTION = 1; + DISABLE_INTERCEPTION = 2; + ENABLE_FIREWALL = 3; + DISABLE_FIREWALL = 4; + RELOAD_FW_RULES = 5; + CHANGE_CONFIG = 6; + ENABLE_RULE = 7; + DISABLE_RULE = 8; + DELETE_RULE = 9; + CHANGE_RULE = 10; + LOG_LEVEL = 11; + STOP = 12; + MONITOR_PROCESS = 13; + STOP_MONITOR_PROCESS = 14; +} + +message StatementValues { + string Key = 1; + string Value = 2; +} + +message Statement { + string Op = 1; + string Name = 2; + repeated StatementValues Values = 3; +} + +message Expressions { + Statement Statement = 1; +} + +message FwRule { + // DEPRECATED: for backward compatibility with iptables + string Table = 1; + string Chain = 2; + + string UUID = 3; + bool Enabled = 4; + uint64 Position = 5; + string Description = 6; + string Parameters = 7; + repeated Expressions Expressions = 8; + string Target = 9; + string TargetParameters = 10; +} + +message FwChain { + string Name = 1; + string Table = 2; + string Family = 3; + string Priority = 4; + string Type = 5; + string Hook = 6; + string Policy = 7; + repeated FwRule Rules = 8; +} + +message FwChains { + // DEPRECATED: backward compatibility with iptables + FwRule Rule = 1; + repeated FwChain Chains = 2; +} + +message SysFirewall { + bool Enabled = 1; + uint32 Version = 2; + repeated FwChains SystemRules = 3; +} + +// client configuration sent on Subscribe() +message ClientConfig { + uint64 id = 1; + string name = 2; + string version = 3; + bool isFirewallRunning = 4; + // daemon configuration as json string + string config = 5; + uint32 logLevel = 6; + repeated Rule rules = 7; + SysFirewall systemFirewall = 8; +} + +// notification sent to the clients (daemons) +message Notification { + uint64 id = 1; + string clientName = 2; + string serverName = 3; + // CHANGE_CONFIG: 2, data: {"default_timeout": 1, ...} + Action type = 4; + string data = 5; + repeated Rule rules = 6; + SysFirewall sysFirewall = 7; +} + +// notification reply sent to the server (GUI) +message NotificationReply { + uint64 id = 1; + NotificationReplyCode code = 2; + string data = 3; +} + +enum NotificationReplyCode { + OK = 0; + ERROR = 1; +} diff --git a/release.sh b/release.sh new file mode 100755 index 0000000..d282411 --- /dev/null +++ b/release.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# nothing to see here, just a utility i use to create new releases ^_^ + +CURRENT_VERSION=$(cat daemon/core/version.go | grep Version | cut -d '"' -f 2) +TO_UPDATE=( + daemon/core/version.go + ui/version.py +) + +echo -n "Current version is $CURRENT_VERSION, select new version: " +read NEW_VERSION +echo "Creating version $NEW_VERSION ...\n" + +for file in "${TO_UPDATE[@]}" +do + echo "Patching $file ..." + sed -i "s/$CURRENT_VERSION/$NEW_VERSION/g" $file + git add $file +done + +git commit -m "Releasing v$NEW_VERSION" +git push + +git tag -a v$NEW_VERSION -m "Release v$NEW_VERSION" +git push origin v$NEW_VERSION + +echo +echo "All done, v$NEW_VERSION released ^_^" diff --git a/screenshots/opensnitch-ui-general-tab-deny.png b/screenshots/opensnitch-ui-general-tab-deny.png new file mode 100644 index 0000000..809fc8e Binary files /dev/null and b/screenshots/opensnitch-ui-general-tab-deny.png differ diff --git a/screenshots/opensnitch-ui-proc-details.png b/screenshots/opensnitch-ui-proc-details.png new file mode 100644 index 0000000..1190a29 Binary files /dev/null and b/screenshots/opensnitch-ui-proc-details.png differ diff --git a/screenshots/screenshot.png b/screenshots/screenshot.png new file mode 100644 index 0000000..5e8c5ab Binary files /dev/null and b/screenshots/screenshot.png differ diff --git a/ui/.gitignore b/ui/.gitignore new file mode 100644 index 0000000..a244087 --- /dev/null +++ b/ui/.gitignore @@ -0,0 +1,5 @@ +*.pyc +build +dist +*.egg-info +__pycache__ diff --git a/ui/LICENSE b/ui/LICENSE new file mode 100644 index 0000000..0b86ea0 --- /dev/null +++ b/ui/LICENSE @@ -0,0 +1,28 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Source: https://github.com/gustavo-iniguez-goya/opensnitch +Upstream-Name: python3-opensnitch-ui +Files: * +Copyright: + 2017-2018 evilsocket + 2019-2020 Gustavo Iñiguez Goia +Comment: Debian packaging is licensed under the same terms as upstream +License: GPL-3.0 + This program is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later + version. + . + This program is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the GNU General Public License for more + details. + . + You should have received a copy of the GNU General Public + License along with this program. If not, If not, see + http://www.gnu.org/licenses/. + . + On Debian systems, the full text of the GNU General Public + License version 3 can be found in the file + '/usr/share/common-licenses/GPL-3'. diff --git a/ui/MANIFEST.in b/ui/MANIFEST.in new file mode 100644 index 0000000..d21350d --- /dev/null +++ b/ui/MANIFEST.in @@ -0,0 +1,5 @@ +recursive-include opensnitch/proto * +recursive-include opensnitch/res * +recursive-include opensnitch/i18n *.qm +recursive-include opensnitch/database/migrations *.sql +include LICENSE diff --git a/ui/Makefile b/ui/Makefile new file mode 100644 index 0000000..0eeda85 --- /dev/null +++ b/ui/Makefile @@ -0,0 +1,19 @@ +all: opensnitch/resources_rc.py + +install: + @pip3 install --upgrade . + +opensnitch/resources_rc.py: translations deps + @pyrcc5 -o opensnitch/resources_rc.py opensnitch/res/resources.qrc + @find opensnitch/proto/ -name 'ui_pb2_grpc.py' -exec sed -i 's/^import ui_pb2/from . import ui_pb2/' {} \; + +translations: + @cd i18n ; make + +deps: + @pip3 install -r requirements.txt + +clean: + @rm -rf *.pyc + @rm -rf opensnitch/resources_rc.py + @find i18n/ -name '*.qm' -delete diff --git a/ui/bin/opensnitch-ui b/ui/bin/opensnitch-ui new file mode 100755 index 0000000..06e52ae --- /dev/null +++ b/ui/bin/opensnitch-ui @@ -0,0 +1,261 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2018 Simone Margaritelli +# 2018 MiWCryptAnalytics +# 2023 munix9 +# 2023 Wojtek Widomski +# 2019-2023 Gustavo Iñiguez Goia +# +# This file is part of OpenSnitch. +# +# OpenSnitch is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenSnitch is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenSnitch. If not, see <http://www.gnu.org/licenses/>. + +from PyQt5 import QtWidgets, QtCore +from PyQt5.QtNetwork import QLocalServer, QLocalSocket + +import sys +import os +import signal +import argparse +import logging + +from concurrent import futures + +import grpc + +dist_path = '/usr/lib/python3/dist-packages/' +if dist_path not in sys.path: + sys.path.append(dist_path) + +from opensnitch.service import UIService +from opensnitch.config import Config +from opensnitch.utils import Themes, Utils, Versions, Message +from opensnitch.utils.xdg import xdg_opensnitch_dir, xdg_current_session + +from opensnitch import auth +import opensnitch.proto as proto +ui_pb2, ui_pb2_grpc = proto.import_() + +app_id = os.path.join(xdg_opensnitch_dir, "io.github.evilsocket.opensnitch") + +def on_exit(): + server.stop(0) + app.quit() + try: + os.remove(app_id) + except: + pass + sys.exit(0) + +def restrict_socket_perms(socket): + """Restrict socket reading to the current user""" + try: + if socket.startswith("unix://") and os.path.exists(socket[7:]): + os.chmod(socket[7:], 0o640) + except Exception as e: + print("Unable to change unix socket permissions:", socket, e) + +def configure_screen_scale_factor(cfg): + """configure qt screen scale: + https://doc.qt.io/qt-5/highdpi.html#high-dpi-support-in-qt + """ + auto_screen_factor = cfg.getBool(Config.QT_AUTO_SCREEN_SCALE_FACTOR, default_value=True) + screen_factor = cfg.getSettings(Config.QT_SCREEN_SCALE_FACTOR) + if screen_factor is None or screen_factor == "": + screen_factor = "1" + + print("QT_AUTO_SCREEN_SCALE_FACTOR:", auto_screen_factor) + os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = str(int(auto_screen_factor)) + if auto_screen_factor is False: + print("QT_SCREEN_SCALE_FACTORS:", screen_factor) + os.environ["QT_SCREEN_SCALE_FACTORS"] = screen_factor + +def configure_qt_platform_plugin(cfg): + qt_plugin = cfg.getSettings(Config.QT_PLATFORM_PLUGIN) + if qt_plugin is None or qt_plugin == "": + return + + print("QT_QPA_PLATFORM:", qt_plugin) + os.environ["QT_QPA_PLATFORM"] = qt_plugin + +def check_environ(): + if xdg_current_session == "": + print(""" + + Warning: XDG_SESSION_TYPE is not set. + If there're no icons on the GUI, please, read the following comment: + https://github.com/evilsocket/opensnitch/discussions/999#discussioncomment-6579273 + +""") + +def supported_qt_version(major, medium, minor): + q = QtCore.QT_VERSION_STR.split(".") + return int(q[0]) >= major and int(q[1]) >= medium and int(q[2]) >= minor + +if __name__ == '__main__': + gui_version, grpcversion, protoversion = Versions.get() + print("\t ~ OpenSnitch GUI -", gui_version, "~") + print("\tprotobuf:", protoversion, "-", "grpc:", grpcversion) + print("-" * 50, "\n") + + parser = argparse.ArgumentParser(description='OpenSnitch UI service.', formatter_class=argparse.RawTextHelpFormatter) + parser.add_argument("--socket", dest="socket", help=''' +Path of the unix socket for the gRPC service (https://github.com/grpc/grpc/blob/master/doc/naming.md). +Default: unix:///tmp/osui.sock + +Examples: + - Listening on Unix socket: opensnitch-ui --socket unix:///tmp/osui.sock + * Use unix:///run/1000/YOUR_USER/opensnitch/osui.sock for better privacy. + - Listening on port 50051, all interfaces: opensnitch-ui --socket "[::]:50051" + ''', metavar="FILE") + parser.add_argument("--socket-auth", dest="socket_auth", help="Auth type: simple, tls-simple, tls-mutual") + parser.add_argument("--tls-ca-cert", dest="tls_ca_cert", help="path to the CA cert") + parser.add_argument("--tls-cert", dest="tls_cert", help="path to the server cert") + parser.add_argument("--tls-key", dest="tls_key", help="path to the server key") + parser.add_argument("--max-clients", dest="serverWorkers", default=10, help="Max number of allowed clients (incoming connections).") + parser.add_argument("--debug", dest="debug", action="store_true", help="Enable debug logs") + parser.add_argument("--debug-grpc", dest="debug_grpc", action="store_true", help="Enable gRPC debug logs") + parser.add_argument("--background", dest="background", action="store_true", help="Start UI in background even, when tray is not available") + + args = parser.parse_args() + + if args.debug: + import faulthandler + faulthandler.enable() + + logging.getLogger().disabled = not args.debug + cfg = Config.get() + configure_screen_scale_factor(cfg) + configure_qt_platform_plugin(cfg) + + if args.debug and args.debug_grpc: + os.environ["GRPC_TRACE"] = "all" + os.environ["GRPC_VERBOSITY"] = "debug" + + if supported_qt_version(5,6,0): + try: + # NOTE: maybe we also need Qt::AA_UseHighDpiPixmaps + QtCore.QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling, True) + except Exception: + pass + + service = None + + try: + Utils.create_socket_dirs() + app = QtWidgets.QApplication(sys.argv) + + localsocket = QLocalSocket() + localsocket.connectToServer(app_id) + + if localsocket.waitForConnected(): + raise Exception("GUI already running, opening its window and exiting.") + else: + localserver = QLocalServer() + localserver.setSocketOptions(QLocalServer.UserAccessOption) + localserver.removeServer(app_id) + localserver.listen(app_id) + + if hasattr(QtCore.Qt, 'AA_UseHighDpiPixmaps'): + app.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps, True) + thm = Themes.instance() + thm.load_theme(app) + + if args.socket == None: + # default + args.socket = "unix:///tmp/osui.sock" + + addr = cfg.getSettings(Config.DEFAULT_SERVER_ADDR) + if addr != None and addr != "": + if addr.startswith("unix://"): + if not os.path.exists(os.path.dirname(addr[7:])): + print("WARNING: unix socket path does not exist, using unix:///tmp/osui.sock, ", addr) + else: + args.socket = addr + else: + args.socket = addr + + maxmsglen = cfg.getMaxMsgLength() + + service = UIService(app, on_exit, start_in_bg=args.background) + check_environ() + localserver.newConnection.connect(service.OpenWindow) + # @doc: https://grpc.github.io/grpc/python/grpc.html#server-object + server = grpc.server(futures.ThreadPoolExecutor(), + options=( + # https://github.com/grpc/grpc/blob/master/doc/keepalive.md + # https://grpc.github.io/grpc/core/group__grpc__arg__keys.html + # send keepalive ping every 5 second, default is 2 hours) + ('grpc.keepalive_time_ms', 5000), + # after 5s of inactivity, wait 20s and close the connection if + # there's no response. + ('grpc.keepalive_timeout_ms', 20000), + ('grpc.keepalive_permit_without_calls', True), + ('grpc.max_send_message_length', maxmsglen), + ('grpc.max_receive_message_length', maxmsglen), + )) + + ui_pb2_grpc.add_UIServicer_to_server(service, server) + + auth_type = auth.Simple + if args.socket_auth != None: + auth_type = args.socket_auth + elif cfg.getSettings(Config.AUTH_TYPE) != None: + auth_type = cfg.getSettings(Config.AUTH_TYPE) + + # grpc python doesn't seem to accept unix:@address to listen on an + # abstract unix socket, so use unix-abstract: and transform it to what + # the Go client understands. + if args.socket.startswith("unix:@"): + parts = args.socket.split("@") + args.socket = "unix-abstract:{0}".format(parts[1]) + + print("Using server address:", args.socket, "auth type:", auth_type) + + if auth_type == auth.Simple or auth_type == "": + server.add_insecure_port(args.socket) + else: + auth_ca_cert = args.tls_ca_cert + auth_cert = args.tls_cert + auth_certkey = args.tls_key + if auth_cert == None: + auth_cert = cfg.getSettings(Config.AUTH_CERT) + if auth_certkey == None: + auth_certkey = cfg.getSettings(Config.AUTH_CERTKEY) + if auth_ca_cert == None: + auth_ca_cert = cfg.getSettings(Config.AUTH_CA_CERT) + + tls_creds = auth.get_tls_credentials(auth_ca_cert, auth_cert, auth_certkey) + if tls_creds == None: + raise Exception("Invalid TLS credentials. Review the server key and cert files.") + server.add_secure_port(args.socket, tls_creds) + + # https://stackoverflow.com/questions/5160577/ctrl-c-doesnt-work-with-pyqt + signal.signal(signal.SIGINT, signal.SIG_DFL) + + # print "OpenSnitch UI service running on %s ..." % socket + server.start() + + restrict_socket_perms(args.socket) + + app.exec_() + + except KeyboardInterrupt: + on_exit() + except Exception as e: + print(e) + finally: + if service: + # finish gracefully, closing notifications channel. + service.close() diff --git a/ui/i18n/Makefile b/ui/i18n/Makefile new file mode 100644 index 0000000..2706005 --- /dev/null +++ b/ui/i18n/Makefile @@ -0,0 +1,37 @@ +SOURCES += ../opensnitch/service.py \ + ../opensnitch/dialogs/prompt.py \ + ../opensnitch/dialogs/preferences.py \ + ../opensnitch/dialogs/ruleseditor.py \ + ../opensnitch/dialogs/processdetails.py \ + ../opensnitch/dialogs/stats.py + +FORMS += ../opensnitch/res/prompt.ui \ + ../opensnitch/res/ruleseditor.ui \ + ../opensnitch/res/preferences.ui \ + ../opensnitch/res/process_details.ui \ + ../opensnitch/res/stats.ui + +#TSFILES contains all *.ts files in locales/ and its subfolders +TSFILES := $(shell find locales/ -type f -name '*.ts') +#QMFILES contains all *.qm files in locales/ and its subfolders +QMFILES := $(shell find locales/ -type f -name '*.qm') +#if QMFILES is empty, we set it to phony target to run unconditionally +ifeq ($(QMFILES),) +QMFILES := "qmfiles" +endif + +all: $(TSFILES) $(QMFILES) + +#if any file from SOURCES or FORMS is older than any file from $(TSFILES) +#or if opensnitch_i18n.pro was manually modified +$(TSFILES): $(SOURCES) $(FORMS) opensnitch_i18n.pro + @pylupdate5 opensnitch_i18n.pro + +#if any of the *.ts files are older that any of the *.qm files +#QMFILES may also be a phony target (when no *.qm exist yet) which will always run +$(QMFILES):$(TSFILES) + @./generate_i18n.sh + for lang in $$(ls locales/); do \ + if [ ! -d ../opensnitch/i18n/$$lang ]; then mkdir -p ../opensnitch/i18n/$$lang ; fi ; \ + cp locales/$$lang/opensnitch-$$lang.qm ../opensnitch/i18n/$$lang/ ; \ + done diff --git a/ui/i18n/README.md b/ui/i18n/README.md new file mode 100644 index 0000000..c10f80d --- /dev/null +++ b/ui/i18n/README.md @@ -0,0 +1,37 @@ + +### Adding a new translation: +0. Install needed packages: `apt install qtchooser pyqt5-dev-tools` +1. mkdir `locales/<YOUR LOCALE>/` + (echo $LANG) +2. add the path to opensnitch_i18n.pro: +``` + TRANSLATIONS += locales/es_ES/opensnitch-es_ES.ts \ + locales/<YOUR LOCALE>/opensnitch-<YOUR LOCALE>.ts +``` +3. make + +### Updating translations: + +1. update translations definitions: + - pylupdate5 opensnitch_i18n.pro + +2. translate a language: + - linguist locales/es_ES/opensnitch-es_ES.ts + +3. create .qm file: + - lrelease locales/es_ES/opensnitch-es_ES.ts -qm locales/es_ES/opensnitch-es_ES.qm + +or: + +1. make +2. linguist locales/es_ES/opensnitch-es_ES.ts +3. make + +### Installing translations (manually) + +In order to test a new translation: + +`mkdir -p /usr/lib/python3/dist-packages/opensnitch/i18n/<YOUR LOCALE>/` +`cp locales/<YOUR LOCALE>/opensnitch-<YOUR LOCALE>.qm /usr/lib/python3/dist-packages/opensnitch/i18n/<YOUR LOCALE>/` + +Note: the destination path may vary depending on your system. diff --git a/ui/i18n/generate_i18n.sh b/ui/i18n/generate_i18n.sh new file mode 100755 index 0000000..0fbbe71 --- /dev/null +++ b/ui/i18n/generate_i18n.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +app_name="opensnitch" +langs_dir="./locales" +lrelease="lrelease" +if ! command -v lrelease >/dev/null; then + # on fedora + lrelease="lrelease-qt5" +fi + +#pylupdate5 opensnitch_i18n.pro + +for lang in $(ls $langs_dir) +do + lang_path="$langs_dir/$lang/$app_name-$lang" + $lrelease $lang_path.ts -qm $lang_path.qm +done diff --git a/ui/i18n/locales/de_DE/opensnitch-de_DE.ts b/ui/i18n/locales/de_DE/opensnitch-de_DE.ts new file mode 100644 index 0000000..bd717dd --- /dev/null +++ b/ui/i18n/locales/de_DE/opensnitch-de_DE.ts @@ -0,0 +1,3345 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="de_DE"> +<context> + <name>Dialog</name> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="34"/> + <source>opensnitch-qt</source> + <translation>OpenSnitch Firewall</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="300"/> + <source>User ID</source> + <translation>User ID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="334"/> + <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source> + <translation><html><head/><body><p><span style=" font-weight:600;">Ausgeführt von</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="647"/> + <source>TextLabel</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="437"/> + <source>Source IP</source> + <translation>Quell-IP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="458"/> + <source>Process ID</source> + <translation>Prozess ID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="601"/> + <source>Destination IP</source> + <translation>Ziel-IP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="622"/> + <source>Dst Port</source> + <translation>Zielport</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="150"/> + <source>Chromium Web Browser</source> + <translation type="obsolete">Chromium-Webbrowser</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="226"/> + <source>(/path/to/bin/chromium)</source> + <translation type="obsolete">(Pfad/zur/bin/chromium)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="271"/> + <source>Chromium Web Browser wants to connect to www.evilsocket.net on tcp port 443. And maybe to www.goodsocket.net on port 344</source> + <translation type="obsolete">Der Chromium-Webbrowser möchte eine Verbindung zu www.evilsocket.net über TCP-Port 443 herstellen. Und möglicherweise zu www.goodsocket.net über Port 344</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="702"/> + <source>from this executable</source> + <translation>von dieser ausführbaren Datei</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="707"/> + <source>from this command line</source> + <translation>von dieser Kommandozeile</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="712"/> + <source>this destination port</source> + <translation>dieser Zielport</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="717"/> + <source>this user</source> + <translation>dieser Benutzer</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="722"/> + <source>this destination ip</source> + <translation>diese Ziel-IP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="751"/> + <source>once</source> + <translation>einmal</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="756"/> + <source>30s</source> + <translation>30s</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="761"/> + <source>5m</source> + <translation>5m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="766"/> + <source>15m</source> + <translation>15m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="771"/> + <source>30m</source> + <translation>30m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="776"/> + <source>1h</source> + <translation>1h</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="706"/> + <source>for this session</source> + <translation type="obsolete">für diese Sitzung</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="786"/> + <source>forever</source> + <translation>für immer</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="346"/> + <source>Deny</source> + <translation type="unfinished">Verweigern</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="337"/> + <source>Allow</source> + <translation>Erlauben</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="865"/> + <source>+</source> + <translation>+</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="781"/> + <source>until reboot</source> + <translation>bis zum Neustart</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="727"/> + <source>from this PID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="809"/> + <source>action</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="14"/> + <source>Firewall</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="55"/> + <source><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="320"/> + <source>Inbound</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="313"/> + <source>Outbound</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="275"/> + <source>Profile</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="375"/> + <source>Allow inbound connections to a port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="378"/> + <source>Allow service (IN)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="397"/> + <source>Exclude outbound connections to a port from being intercepted</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="406"/> + <source>Allow service (OUT)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="426"/> + <source>New rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="431"/> + <source>Close</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="14"/> + <source>Firewall rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="26"/> + <source>Node</source> + <translation type="unfinished">Knoten</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="38"/> + <source>Enable</source> + <translation type="unfinished">Aktivieren</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="50"/> + <source>Description</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="90"/> + <source>Simple</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="154"/> + <source>Add new condition</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="177"/> + <source>Remove selected condition</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="233"/> + <source>Direction</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="248"/> + <source>IN</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="257"/> + <source>OUT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="223"/> + <source>Action</source> + <translation type="unfinished">Aktion</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="285"/> + <source>ACCEPT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="294"/> + <source>DROP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="303"/> + <source>REJECT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="312"/> + <source>RETURN</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="442"/> + <source>Clear</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="453"/> + <source>Delete</source> + <translation type="unfinished">Löschen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="464"/> + <source>Save</source> + <translation type="unfinished">Speichern</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="475"/> + <source>Add</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="266"/> + <source>FORWARD</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="271"/> + <source>PREROUTING</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="276"/> + <source>POSTROUTING</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="321"/> + <source>QUEUE</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="330"/> + <source>DNAT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="335"/> + <source>SNAT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="340"/> + <source>REDIRECT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="359"/> + <source>depending on the Action (i.e.: target), the syntaxis of the parameters will vary. +Some examples: + +QUEUE -> num 0 (or 1, 2, ...) +REDIRECT, TPROXY, DNAT, SNAT, MASQUERADE: + to :22 + to 192.168.1.254:8080 + to 192.168.1.254 + to 1024-2048 (masquerade)</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>PreferencesDialog</name> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="14"/> + <source>Preferences</source> + <translation>Einstellungen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="484"/> + <source>UI</source> + <translation>Popup-Fenster</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="54"/> + <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Dieses Zeitlimit ist der Countdown, der angezeigt wird, wenn ein Popup-Fenster angezeigt wird.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="466"/> + <source>Default timeout</source> + <translation>Standardzeitlimit</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="340"/> + <source>Pop-up default duration</source> + <translation>Popup-Standarddauer</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="866"/> + <source>Default duration</source> + <translation>Standarddauer</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="162"/> + <source>Pop-up default action</source> + <translation type="obsolete">Popup-Standardaktion</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="483"/> + <source>Default action</source> + <translation type="obsolete">Standardaktion</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="323"/> + <source>Default target</source> + <translation>Standardfilterung</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="179"/> + <source>center</source> + <translation>mittig</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="184"/> + <source>top right</source> + <translation>oben rechts</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="189"/> + <source>bottom right</source> + <translation>unten rechts</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="194"/> + <source>top left</source> + <translation>oben links</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="199"/> + <source>bottom left</source> + <translation>unten links</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="167"/> + <source>Prompt dialog default position on screen</source> + <translation type="obsolete">Grundposition</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="283"/> + <source>by executable</source> + <translation>nach ausführbarer Datei</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="288"/> + <source>by command line</source> + <translation>nach Befehl</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="293"/> + <source>by destination port</source> + <translation>nach Zielport</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="298"/> + <source>by destination ip</source> + <translation>nach Ziel-IP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="303"/> + <source>by user id</source> + <translation>nach UID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="970"/> + <source>once</source> + <translation>einmal</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="219"/> + <source>30s</source> + <translation>30s</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="224"/> + <source>5m</source> + <translation>5m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="229"/> + <source>15m</source> + <translation>15m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="234"/> + <source>30m</source> + <translation>30m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="239"/> + <source>1h</source> + <translation>1h</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="240"/> + <source>for this session</source> + <translation type="obsolete">für diese Sitzung</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="249"/> + <source>forever</source> + <translation>für immer</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1012"/> + <source>deny</source> + <translation>verweigern</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1021"/> + <source>allow</source> + <translation>erlauben</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="406"/> + <source>Disable pop-ups, only display an alert</source> + <translation type="obsolete">Popups deaktivieren, nur eine Warnung anzeigen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="823"/> + <source>Nodes</source> + <translation>Knoten</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="829"/> + <source>Process monitor method</source> + <translation>Prozessüberwachungsmethode</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="863"/> + <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source> + <translation><html><head/><body><p>Die Standarddauer gilt, wenn keine Benutzeroberfläche verbunden ist.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="988"/> + <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source> + <translation><html><head/><body><p>Knotenadresse.</p><p>Standardmäßig: unix: ///tmp/osui.sock (unix: // ist erforderlich, wenn ein Unix-Socket vorhanden ist)</p><p>Es kann sich auch um eine IP mit diesem Format handeln: 127.0.0.1:50051, 192.168.1.122:12345 usw.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="991"/> + <source>Address</source> + <translation>Adresse</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1131"/> + <source>Default log level</source> + <translation>Standardprotokollstufe</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1039"/> + <source>Version</source> + <translation>Version</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="902"/> + <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source> + <translation><html><head/><body><p>Die Standardaktion wird angewendet, wenn keine Benutzeroberfläche verbunden ist.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="846"/> + <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source> + <translation><html><head/><body><p>Protokolldatei, in welche die Protokolle geschrieben werden sollen.<br/></p><p>/dev/stdout schreibt die Protokolle in die Standardausgabe des Dienstes..</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="849"/> + <source>Log file</source> + <translation>Logdatei</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="578"/> + <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons.</p><p>The pop-up dialog will only contain information about the network connection.</p></body></html></source> + <translation type="obsolete">Wenn Sie diese Option aktivieren, werden Sie von OpenSnitch aufgefordert, Verbindungen zu akzeptieren oder zu verweigern, denen aus verschiedenen Gründen keine PID zugeordnet ist. + +Das Popup-Fenster enthält nur Informationen zur Verbindung. + +Hinweis: Diese Verbindungen müssen nicht darauf hinweisen, dass etwas Verdächtiges passiert. Einfach +ist, dass wir die PID nicht entdeckt haben (zum Beispiel Verbindungen, die nicht vom Computer stammen, oder fehlerhafte Pakete).</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="581"/> + <source>Intercept Unknown Connections</source> + <translation type="obsolete">Unbekannte Verbindungen abfangen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="921"/> + <source>HostName</source> + <translation>HostName</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1090"/> + <source>unix:///tmp/osui.sock</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="975"/> + <source>until restart</source> + <translation>bis zum Neustart</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="980"/> + <source>always</source> + <translation>immer</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1102"/> + <source>/var/log/opensnitchd.log</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1107"/> + <source>/dev/stdout</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="879"/> + <source>Apply configuration to all nodes</source> + <translation>Konfiguration auf alle Knoten anwenden</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1146"/> + <source>Database</source> + <translation>Datenbank</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="630"/> + <source>Database name</source> + <translation type="obsolete">Datenbankname</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1181"/> + <source>In memory</source> + <translation>Im Speicher</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1186"/> + <source>File</source> + <translation>Datei</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="669"/> + <source>/path/to/the/file.db</source> + <translation type="obsolete">/Pfad/zu/der/Datei.db</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1482"/> + <source>Close</source> + <translation>Schließen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1493"/> + <source>Apply</source> + <translation>Anwenden</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1504"/> + <source>Save</source> + <translation>Speichern</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="244"/> + <source>until reboot</source> + <translation>Bis zum Neustart</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="356"/> + <source>The advanced view allows you to easily select multiple fields to filter connections</source> + <translation>In der erweiterten Ansicht können Sie ganz einfach mehrere Felder auswählen, um Verbindungen zu filtern</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="359"/> + <source>Show advanced view by default</source> + <translation>Standardmäßig erweiterte Ansicht anzeigen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="653"/> + <source>Action</source> + <translation>Aktion</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="375"/> + <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source> + <translation><html><head/><body><p>Wenn diese Option aktiviert ist, werden die Pop-ups mit aktiver erweiterter Ansicht angezeigt.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="343"/> + <source>Duration</source> + <translation>Dauer</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="263"/> + <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source> + <translation><html><head/><body><p>Wenn ein neues Popup-Fenster in seiner einfachsten Form angezeigt wird, können Sie standardmäßig Verbindungen oder Anwendungen nach einer Eigenschaft der Verbindung (ausführbare Datei, Port, IP usw.) filtern.</p><p>Mit diesen Optionen können Sie mehrere Felder auswählen, nach denen Verbindungen gefiltert werden sollen.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="266"/> + <source>Filter connections also by:</source> + <translation>Verbindungen auch filtern nach:</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="110"/> + <source>If checked, this field will be selected when a pop-up is displayed</source> + <translation>Wenn aktiviert, wird dieses Feld ausgewählt, wenn ein Popup angezeigt wird</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="81"/> + <source>User ID</source> + <translation>User ID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="97"/> + <source>Destination port</source> + <translation>Ziel Port</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="113"/> + <source>Destination IP</source> + <translation>Ziel-IP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="463"/> + <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source> + <translation><html><head/><body><p>Dieses Timeout ist der Countdown, den Sie sehen, wenn ein Popup-Dialogfeld angezeigt wird.</p><p>Wenn das Popup nicht beantwortet wird, werden die Standardoptionen angewendet.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1200"/> + <source>Database type</source> + <translation>Datenbanktyp</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1207"/> + <source>Select</source> + <translation>Auswählen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="159"/> + <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source> + <translation><html><head/><body><p>Popup-Standardaktion.</p><p>Wenn eine neue ausgehende Verbindung hergestellt werden soll, wird diese Aktion standardmäßig ausgewählt. Wenn das Timeout auftritt, wird diese Option angewendet.</p><p><br/></p><p>Während ein Pop-up den Benutzer auffordert, eine Verbindung zuzulassen oder abzulehnen:</p><p>1. neue ausgehende Verbindungen werden verweigert.</p><p>2. bekannte Verbindungen werden nach den vom Benutzer definierten Regeln zugelassen oder verweigert.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="905"/> + <source>Default action when the GUI is disconnected</source> + <translation>Standardaktion, wenn die GUI getrennt ist</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1001"/> + <source>Debug invalid connections</source> + <translation>Debugge ungültige Verbindungen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="39"/> + <source>Pop-ups</source> + <translation>Pop-ups</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="64"/> + <source>Default options</source> + <translation>Standardoptionen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="330"/> + <source>Default position on screen</source> + <translation>Standardposition auf dem Bildschirm</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="769"/> + <source>any temporary rules</source> + <translation>jede temporäre Regel</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="487"/> + <source><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Wenn diese Option ausgewählt ist, werden die Regeln der ausgewählten Dauer nicht zur Liste der temporären Regeln in der GUI hinzugefügt.</p><p><br/></p><p>Temporäre Regeln sind weiterhin gültig und Sie können sie verwenden, wenn Sie dazu aufgefordert werden, eine neue Verbindung zuzulassen/zu verweigern.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="490"/> + <source>Don't save rules of duration</source> + <translation type="obsolete">Speichern Sie keine Regeln der Dauer</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="589"/> + <source>Time</source> + <translation>Zeit</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="669"/> + <source>Destination</source> + <translation>Ziel</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="637"/> + <source>Protocol</source> + <translation>Protokoll</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="685"/> + <source>Process</source> + <translation>Prozess</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="605"/> + <source>Rule</source> + <translation>Regel</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="621"/> + <source>Node</source> + <translation>Knoten</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="723"/> + <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using wireguard.</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Wenn diese Option aktiviert ist, fordert Opensnitch Sie aus verschiedenen Gründen auf, Verbindungen zuzulassen oder zu verweigern, die keine zugeordnete PID haben, hauptsächlich aufgrund von Verbindungen mit schlechtem Status.</p><p>Der Popup-Dialog enthält nur Informationen über die Netzwerkverbindung.</p><p>Es gibt jedoch einige Szenarien, in denen dies gültige Verbindungen sind, z. B. beim Einrichten eines VPN mit Wireguard.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="577"/> + <source>Events tab columns</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="308"/> + <source>by PID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="473"/> + <source>Disable pop-ups, only display a notification</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="496"/> + <source>Desktop notifications</source> + <translation>Desktop-Benachrichtigungen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="514"/> + <source>Use system notifications</source> + <translation>Systembenachrichtigungen verwenden</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="530"/> + <source>Use Qt notifications</source> + <translation>Qt-Benachrichtigungen verwenden</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="559"/> + <source>Test</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="998"/> + <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1294"/> + <source>minutes</source> + <translation>Minuten</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1326"/> + <source>Minutes between events purges</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1352"/> + <source>days</source> + <translation>Tage</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1365"/> + <source>Maximum days of events to keep</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="147"/> + <source>reject</source> + <translation>Ablehnen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="716"/> + <source>System</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="695"/> + <source>Command line</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="708"/> + <source>Theme</source> + <translation>Design</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="748"/> + <source>Rules</source> + <translation type="unfinished">Regeln</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="756"/> + <source>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI. + +Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="761"/> + <source>Don't save/Delete rules of duration</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="779"/> + <source>30s or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="784"/> + <source>5m or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="789"/> + <source>15m or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="794"/> + <source>30m or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="799"/> + <source>1h or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="724"/> + <source>Language</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>ProcessDetailsDialog</name> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="14"/> + <source>Process details</source> + <translation>Prozessdetails</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="61"/> + <source>loading...</source> + <translation>wird geladen...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="81"/> + <source>CWD: loading...</source> + <translation>CWD: Laden...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="93"/> + <source>mem stats: loading...</source> + <translation>Speicherstatistik: Laden ...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="121"/> + <source>Status</source> + <translation>Status</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="135"/> + <source>Open files</source> + <translation>Dateien öffnen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="149"/> + <source>I/O Statistics</source> + <translation>I/O Statistiken</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="163"/> + <source>Memory mapped files</source> + <translation>Dateien in den Speicher geladen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="177"/> + <source>Stack</source> + <translation>Stapel</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="191"/> + <source>Environment variables</source> + <translation>Umgebungsvariablen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="210"/> + <source>Application pids</source> + <translation>Anwendungs-PIDs</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="240"/> + <source>Start or stop monitoring this process</source> + <translation>Starten oder beenden Sie die Überwachung dieses Prozesses</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="256"/> + <source>Close</source> + <translation>Schließen</translation> + </message> +</context> +<context> + <name>RulesDialog</name> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="20"/> + <source>Rule</source> + <translation>Regel</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> + <source>Node</source> + <translation>Knoten</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> + <source>Apply rule to all nodes</source> + <translation>Regel auf alle Knoten anwenden</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="379"/> + <source>From this command line</source> + <translation>Von dieser Kommandozeile</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="472"/> + <source>From this executable</source> + <translation>Von dieser ausführbaren Datei</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="56"/> + <source>Action</source> + <translation>Aktion</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="138"/> + <source>/path/to/executable, .*/bin/executable[0-9\.]+$, ...</source> + <translation type="obsolete">/Pfad/zur/ausführbaren/Datei, .*/bin/executable[0-9\.]+$, ...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="610"/> + <source>To this IP / Network</source> + <translation>Zu dieser IP / Netzwerk</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="97"/> + <source>once</source> + <translation>einmal</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="797"/> + <source>30s</source> + <translation type="obsolete">30s</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="802"/> + <source>5m</source> + <translation type="obsolete">5m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="807"/> + <source>15m</source> + <translation type="obsolete">15m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="812"/> + <source>30m</source> + <translation type="obsolete">30m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="817"/> + <source>1h</source> + <translation type="obsolete">1h</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="230"/> + <source>until restart</source> + <translation type="obsolete">bis zum Neustart</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="132"/> + <source>always</source> + <translation>immer</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="902"/> + <source>To this port</source> + <translation>Zu diesem Port</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="372"/> + <source>From this user ID</source> + <translation>Von dieser Benutzer-ID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/> + <source>Commas or spaces are not allowed to specify multiple domains. + +Use regular expressions instead: +.*(opensnitch|duckduckgo).com +.*\.google.com + +or a single domain: +www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... +gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source> + <translation>Kommas oder Leerzeichen dürfen nicht mehrere Domänen angeben. + +Verwenden Sie stattdessen reguläre Ausdrücke: +.*(opensnitch|duckduckgo).com +.*\.google.com + +oder eine einzelne Domain: +www.gnu.org - es wird nur mit www.gnu.org, noch ftp.gnu.org oder www2.gnu.org übereinstimmen, ... +gnu.org - es wird nur mit gnu.org, www.gnu.org oder ftp.gnu.org übereinstimmen, ...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="603"/> + <source>www.domain.org, .*\.domain.org</source> + <translation>www.domain.org, .*\.domain.org</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="526"/> + <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source> + <translation><html><head/><body><p>Es sind nur TCP-, UDP- oder UDPLITE-Optionen zulässig.</p><p>Sie können reguläre Ausdrücke verwenden zu diesen Optionen, zum Beispiel TCP oder UDP: ^ (TCP|UDP)$</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="532"/> + <source>TCP</source> + <translation>TCP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="338"/> + <source>UDP</source> + <translation type="obsolete">UDP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="343"/> + <source>UDPLITE</source> + <translation type="obsolete">UDPLITE</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="348"/> + <source>TCP6</source> + <translation type="obsolete">TCP6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="353"/> + <source>UDP6</source> + <translation type="obsolete">UDP6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="358"/> + <source>UDPLITE6</source> + <translation type="obsolete">UDPLITE6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="760"/> + <source>You can specify a single IP: +- 192.168.1.1 + +or a regular expression: +- 192\.168\.1\.[0-9]+ + +multiple IPs: +- ^(192\.168\.1\.1|172\.16\.0\.1)$ + +You can also specify a subnet: +- 192.168.1.0/24 + +Note: Commas or spaces are not allowed to separate IPs or networks.</source> + <translation>Sie können eine IP angeben: +- 192.168.1.1 + +oder ein regulärer Ausdruck: +- 192\.168\.1\.[0-9]+ + +mehrere IPs: +- ^(192\.168\.1\.1|172\.16\.0\.1)$ + +Sie können auch ein Subnetz angeben: +- 192.168.1.0/24 + +Hinweis: Kommas und Leerzeichen dürfen keine IPs oder Netzwerke angeben.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="459"/> + <source>LAN</source> + <translation type="obsolete">LAN</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="464"/> + <source>127.0.0.0/8</source> + <translation type="obsolete">127.0.0.0/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="469"/> + <source>192.168.0.0/24</source> + <translation type="obsolete">192.168.0.0/24</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="474"/> + <source>192.168.1.0/24</source> + <translation type="obsolete">192.168.1.0/24</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> + <source>192.168.2.0/24</source> + <translation type="obsolete">192.168.2.0/24</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="484"/> + <source>192.168.0.0/16</source> + <translation type="obsolete">192.168.0.0/16</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="489"/> + <source>169.254.0.0/16</source> + <translation type="obsolete">169.254.0.0/16</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="494"/> + <source>172.16.0.0/12</source> + <translation type="obsolete">172.16.0.0/12</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="499"/> + <source>10.0.0.0/8</source> + <translation type="obsolete">10.0.0.0/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="504"/> + <source>::1/128</source> + <translation type="obsolete">::1/128</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="509"/> + <source>fc00::/7</source> + <translation type="obsolete">fc00::/7</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="514"/> + <source>ff00::/8</source> + <translation type="obsolete">ff00::/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="519"/> + <source>fe80::/10</source> + <translation type="obsolete">fe80::/10</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="524"/> + <source>fd00::/8</source> + <translation type="obsolete">fd00::/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="89"/> + <source>Duration</source> + <translation>Dauer</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="633"/> + <source>Protocol</source> + <translation>Protokoll</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="750"/> + <source>To this host</source> + <translation>Zu diesem Host</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="151"/> + <source>Deny</source> + <translation>Verweigern</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="191"/> + <source>Allow</source> + <translation>Erlauben</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="245"/> + <source>Name</source> + <translation type="unfinished">Name</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="207"/> + <source>Enable</source> + <translation>Aktivieren</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="238"/> + <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them. + +000-allow-localhost +001-deny-broadcast +...</source> + <translation>Regeln werden in alphabetischer Reihenfolge überprüft, daher können Sie diese so benennen, um sie zu priorisieren. + +000-allow-localhost +0001-Deny-Broadcast +...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="773"/> + <source>leave blank to autocreate</source> + <translation type="obsolete">Lassen Sie das Feld leer, um den Namen automatisch zuzuweisen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="214"/> + <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one. + +You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example: + +[x] Priority - 000-priority-rule +[ ] Priority - 001-less-priority-rule</source> + <translation>Wenn Sie diese Option aktivieren, hat diese Regel bei der Bewertung Vorrang vor den übrigen Regeln. Danach werden keine Regeln mehr überprüft. + +Sie müssen die Regel so benennen, dass sie zuerst überprüft wird, da sie in alphabetischer Reihenfolge überprüft wird. Zum Beispiel: + +[x] Priorität - 000-Prioritätsregel +[] Priorität - 001-Regel mit weniger Priorität</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="222"/> + <source>Priority rule</source> + <translation>Prioritätsregel</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1092"/> + <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source> + <translation><html><head/><body><p>Standardmäßig wird bei den Feldern einer Regel NICHT zwischen Groß- und Kleinschreibung unterschieden, d. H.; Wenn ein Prozess versucht, auf gOOgle.CoM zuzugreifen, und Sie eine Regel zum Verweigern haben. * Google.com, wird die Verbindung blockiert.<br/></p><p>Wenn Sie diese Option aktivieren und gOOgle.CoM GENAU blockieren möchten, müssen Sie dies im Regelfeld angeben, also die genaue Domain, die Sie filtern möchten (in diesem Fall: gOOgle.CoM).</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1095"/> + <source>Case-sensitive</source> + <translation>Groß- und Kleinschreibung beachten</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="686"/> + <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Sie können mehrere Ports mit regulären Ausdrücken angeben:</p><p><br/></p><p>- 53, 80 oder 443: +</p><p>^ (53|80|443)$</p><p><br/></p><p>- 53, 443 oder 5551, 5552, 5553 usw.:</p><p>^ (53|443|555[0-9])$</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="127"/> + <source>until reboot</source> + <translation>Bis zum Neustart</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="980"/> + <source>To this list of domains</source> + <translation>Zu dieser Domainliste</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="539"/> + <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Wählen Sie ein Verzeichnis mit Domänenlisten aus, die blockiert oder zugelassen werden sollen.</p><p>Legen Sie in diesem Verzeichnis Dateien mit einer beliebigen Erweiterung ab, die Listen von Domänen enthalten.</p><p><br/>Das Format jedes Eintrags einer Liste ist wie folgt (Hosts-Format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="346"/> + <source>Applications</source> + <translation>Anwendungen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="389"/> + <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="399"/> + <source>From this PID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="491"/> + <source>Network</source> + <translation>Netzwerk</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="932"/> + <source>List of domains/IPs</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="938"/> + <source>To this list of network ranges</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="945"/> + <source>To this list of IPs</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="971"/> + <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1006"/> + <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1034"/> + <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1049"/> + <source>To this list of domains +(regular expressions)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1076"/> + <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="168"/> + <source>Reject</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1151"/> + <source>Description...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="355"/> + <source><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="365"/> + <source>Is regular expression</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> + <source>is regular expression</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="863"/> + <source>Network interface</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1086"/> + <source>More</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1102"/> + <source>Don't log connections that match this rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1105"/> + <source>Don't log connections</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="148"/> + <source>Deny will just discard the connection</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="165"/> + <source>Reject will drop the connection, and kill the socket that initiated it</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="185"/> + <source>Allow will allow the connection</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="566"/> + <source>ICMP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="571"/> + <source>ICMP6</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="576"/> + <source>SCTP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="581"/> + <source>SCTP6</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="743"/> + <source>From this IP / Network</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="872"/> + <source>From this port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="918"/> + <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>StatsDialog</name> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="34"/> + <source>OpenSnitch Network Statistics</source> + <translation>OpenSnitch-Netzwerkstatistik</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="287"/> + <source>Save to CSV</source> + <translation type="obsolete">Als CSV exportieren.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="297"/> + <source>Ctrl+S</source> + <translation type="obsolete">Strg+S</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="333"/> + <source>Create a new rule</source> + <translation>Erstellen Sie eine neue Regel</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="376"/> + <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="413"/> + <source>Status</source> + <translation>Status</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1793"/> + <source>-</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="451"/> + <source>Start or Stop interception</source> + <translation>Abfangen starten oder stoppen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="496"/> + <source>Events</source> + <translation>Ereignisse</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="94"/> + <source>Filter</source> + <translation>Filter</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="107"/> + <source>Allow</source> + <translation>Erlauben</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="116"/> + <source>Deny</source> + <translation>Verweigern</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="143"/> + <source>Ex.: firefox</source> + <translation>Beispiel: firefox</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="205"/> + <source>50</source> + <translation>50</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="210"/> + <source>100</source> + <translation>100</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="215"/> + <source>200</source> + <translation>200</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="220"/> + <source>300</source> + <translation>300</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="826"/> + <source>Nodes</source> + <translation>Knoten</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="554"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Addr column to view details of a node)</span></p></body></html></source> + <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(Doppelklicken Sie auf die Addressenspalte, um Details eines Knotens anzuzeigen)</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1700"/> + <source>Rules</source> + <translation>Regeln</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="995"/> + <source>enable</source> + <translation>aktivieren</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="671"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Name column to view details of a rule)</span></p></body></html></source> + <translation type="obsolete">(Doppelklicken Sie auf die Namenspalte, um Details einer Regel anzuzeigen.)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="692"/> + <source>search rule name</source> + <translation type="obsolete">Suchregelname</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="782"/> + <source>Application rules</source> + <translation>Anwendungsregeln</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="936"/> + <source>Permanent</source> + <translation>Dauerhaft</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="945"/> + <source>Temporary</source> + <translation>Temporär</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1063"/> + <source>Hosts</source> + <translation>Hosts</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1364"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click to view details of an item)</span></p></body></html></source> + <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(Doppelklicken Sie auf eine Element, um Details anzuzeigen.)</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1153"/> + <source>Applications</source> + <translation>Anwendungen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1266"/> + <source>Addresses</source> + <translation>Adressen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1356"/> + <source>Ports</source> + <translation>Ports</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1440"/> + <source>Users</source> + <translation>Benutzer</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1544"/> + <source>Connections</source> + <translation>Verbindungen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1596"/> + <source>Dropped</source> + <translation>Abgelehnt</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1648"/> + <source>Uptime</source> + <translation>Betriebszeit</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1767"/> + <source>Version</source> + <translation>Version</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="233"/> + <source>Delete all intercepted events</source> + <translation>Löschen Sie alle abgefangenen Ereignisse</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1022"/> + <source>Edit rule</source> + <translation>Regel bearbeiten</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1039"/> + <source>Delete rule</source> + <translation>Regel löschen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="926"/> + <source>Delete all intercepted hosts</source> + <translation type="obsolete">Löschen Sie alle abgefangenen Hosts</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1051"/> + <source>Delete all intercepted applications</source> + <translation type="obsolete">Löschen Sie alle abgefangenen Anwendungen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1159"/> + <source>Delete all intercepted addresses</source> + <translation type="obsolete">Löschen Sie alle abgefangenen Adressen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1261"/> + <source>Delete all intercepted ports</source> + <translation type="obsolete">Löschen Sie alle abgefangenen Ports</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1371"/> + <source>Delete all intercepted users</source> + <translation type="obsolete">Löschen Sie alle abgefangenen Benutzer</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="699"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click on a row to view details of a rule)</span></p></body></html></source> + <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(Doppelklicken Sie auf eine Zeile, um Details zu einer Regel anzuzeigen)</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="912"/> + <source>Delete connections that matched this rule</source> + <translation type="obsolete">Verbindungen löschen, die dieser Regel entsprechen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="927"/> + <source>All applications</source> + <translation>Alle Anwendungen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="125"/> + <source>Reject</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="180"/> + <source>0</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="777"/> + <source>2</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="954"/> + <source>System rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="637"/> + <source>Delete this node</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="653"/> + <source>Show the preferences of this node</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="669"/> + <source>Start or stop interception of this node</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>contextual_menu</name> + <message> + <location filename="../../../opensnitch/service.py" line="47"/> + <source>Statistics</source> + <translation>Statistiken</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="50"/> + <source>Help</source> + <translation>Hilfe</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="51"/> + <source>Close</source> + <translation>Schließen</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="48"/> + <source>Enable</source> + <translation>Aktivieren</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="49"/> + <source>Disable</source> + <translation>Deaktivieren</translation> + </message> +</context> +<context> + <name>firewall</name> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="91"/> + <source>Configuration applied.</source> + <translation type="unfinished">Konfiguration angewendet.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="404"/> + <source>Error: {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="193"/> + <source>Applying changes...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="230"/> + <source>Error getting INPUT chain policy</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="237"/> + <source>Error getting OUTPUT chain policy</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="290"/> + <source>In order to configure firewall rules from the GUI, we need to use 'nftables' instead of 'iptables'</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="304"/> + <source>Enabling firewall...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="306"/> + <source>Disabling firewall...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="71"/> + <source>Dest Port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="72"/> + <source>Source Port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="73"/> + <source>Dest IP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="74"/> + <source>Source IP</source> + <translation type="unfinished">Quell-IP</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="75"/> + <source>Input interface</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="76"/> + <source>Output interface</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="77"/> + <source>Set conntrack mark</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="78"/> + <source>Match conntrack mark</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="79"/> + <source>Match conntrack state(s)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="80"/> + <source>Set mark on packet</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="81"/> + <source>Match packet information</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="87"/> + <source>Bandwidth quotas</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="89"/> + <source>Rate limit connections</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="373"/> + <source>Your protobuf version is incompatible, you need to install protobuf 3.8.0 or superior +(pip3 install --ignore-installed protobuf==3.8.0)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="397"/> + <source>Rule deleted</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="401"/> + <source>Rule added</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="420"/> + <source>You can use ',' or '-' to specify multiple ports/IPs or ranges/values:<br><br>ports: 22 or 22,443 or 50000-60000<br>IPs: 192.168.1.1 or 192.168.1.30-192.168.1.130<br>Values: echo-reply,echo-request<br>Values: new,established,related</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="440"/> + <source>Deleting rule, wait</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="443"/> + <source>Error updating rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="483"/> + <source>Adding rule, wait</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="492"/> + <source><select a statement></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="787"/> + <source>Equal</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="788"/> + <source>Not equal</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="789"/> + <source>Greater or equal than</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="790"/> + <source>Greater than</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="791"/> + <source>Less or equal than</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="792"/> + <source>Less than</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1350"/> + <source>Firewall rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="885"/> + <source>Simple</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="890"/> + <source>Advanced</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1046"/> + <source>This rule is not supported yet.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1111"/> + <source>Exclude service</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1123"/> + <source>Allow inbound connections to the selected port.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1125"/> + <source>Allow outbound connections to the selected port.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1201"/> + <source>select a statement.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1217"/> + <source>value cannot be 0 or empty.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1229"/> + <source>the value format is 1024/kbytes (or bytes, mbytes, gbytes)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1240"/> + <source>the value format is 1024/kbytes/second (or bytes, mbytes, gbytes)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1243"/> + <source>rate-limit not valid, use: bytes, kbytes, mbytes or gbytes.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1245"/> + <source>time-limit not valid, use: second, minute, hour or day</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1293"/> + <source>port not valid.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="108"/> + <source> +Supported formats: + + - Simple: 23 + - Ranges: 80-1024 + - Multiple ports: 80,443,8080 +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="134"/> + <source> +Supported formats: + + - Simple: 1.2.3.4 + - IP ranges: 1.2.3.100-1.2.3.200 + - Network ranges: 1.2.3.4/24 +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="147"/> + <source>Match input interface. Regular expressions not allowed.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="154"/> + <source>Match output interface. Regular expressions not allowed.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="161"/> + <source>Set a conntrack mark on the connection, in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="171"/> + <source>Match a conntrack mark of the connection, in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="178"/> + <source>Match conntrack states. + +Supported formats: + - Simple: new + - Multiple states separated by commas: related,new +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="193"/> + <source> +Match packet's metainformation. + +Value must be in decimal format, except for the "l4proto" option. +For l4proto it can be a lower case string, for example: + tcp + udp + icmp, + etc + +If the value is decimal for protocol or lproto, it'll use it as the code of +that protocol. +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="213"/> + <source>Set a mark on the packet matching the specified conditions. The value is in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="221"/> + <source> +Match ICMP codes. + +Supported formats: + - Simple: echo-request + - Multiple separated by commas: echo-request,echo-reply +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="234"/> + <source> +Match ICMPv6 codes. + +Supported formats: + - Simple: echo-request + - Multiple separated by commas: echo-request,echo-reply +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="247"/> + <source>Print a message when this rule matches a packet.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="254"/> + <source> +Apply quotas on connections. + +For example when: + - "quota over 10/mbytes" -> apply the Action defined (DROP) + - "quota until 10/mbytes" -> apply the Action defined (ACCEPT) + +The value must be in the format: VALUE/UNITS, for example: + - 10mbytes, 1/gbytes, etc +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="286"/> + <source> +Apply limits on connections. + +For example when: + - "limit over 10/mbytes/minute" -> apply the Action defined (DROP, ACCEPT, etc) + (When there're more than 10MB per minute, apply an Action) + + - "limit until 10/mbytes/hour" -> apply the Action defined (ACCEPT) + +The value must be in the format: VALUE/UNITS/TIME, for example: + - 10/mbytes/minute, 1/gbytes/hour, etc +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="607"/> + <source>num</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="621"/> + <source>to</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>menu_close</name> + <message> + <location filename="../../../opensnitch/service.py" line="131"/> + <source>Close</source> + <translation type="obsolete">Cerrar</translation> + </message> +</context> +<context> + <name>menu_help</name> + <message> + <location filename="../../../opensnitch/service.py" line="126"/> + <source>Help</source> + <translation type="obsolete">Ayuda</translation> + </message> +</context> +<context> + <name>menu_statistics</name> + <message> + <location filename="../../../opensnitch/service.py" line="120"/> + <source>Statistics</source> + <translation type="obsolete">Eventos</translation> + </message> +</context> +<context> + <name>messages</name> + <message> + <location filename="../../../opensnitch/service.py" line="281"/> + <source>Info</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="285"/> + <source>Error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="289"/> + <source>Warning</source> + <translation type="unfinished">Warnung</translation> + </message> +</context> +<context> + <name>notifications</name> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="654"/> + <source>System notifications are not available, you need to install python3-notify2.</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>popups</name> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="115"/> + <source>Allow</source> + <translation>Erlauben</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="114"/> + <source>Deny</source> + <translation>Verweigern</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/> + <source>forever</source> + <translation>für immer</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="331"/> + <source>Outgoing connection</source> + <translation>Ausgehende Verbindung</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="336"/> + <source>Process launched from:</source> + <translation>Prozess ausgeführt von:</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="377"/> + <source>from this command line</source> + <translation>von dieser Kommandozeile</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="373"/> + <source>from this executable</source> + <translation>von dieser ausführbaren Datei</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="208"/> + <source>Unknown process</source> + <translation type="obsolete">Unbekannter Prozess</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="50"/> + <source>until reboot</source> + <translation>Bis zum Neustart</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="379"/> + <source>to port {0}</source> + <translation>zum Port {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="222"/> + <source><b>%s</b> is connecting to <b>%s</b> on %s port %d</source> + <translation type="obsolete"><b>%s</b> stellt eine Verbindung zu <b>%s</b> an Port%s %d her</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="228"/> + <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source> + <translation type="obsolete"><b>Remote-Prozess <b>%s</b>, der auf <b>%s</b> ausgeführt wird, stellt eine Verbindung zu <b>%s</b> auf %s Port %d her</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="442"/> + <source>to {0}</source> + <translation>zu {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="382"/> + <source>from user {0}</source> + <translation>UID {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="399"/> + <source>to {0}.*</source> + <translation>zu {0}.*</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="452"/> + <source>to *.{0}</source> + <translation>zu *.{0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="378"/> + <source>to *{0}</source> + <translation type="obsolete">zu *{0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="486"/> + <source><b>Remote</b> process %s running on <b>%s</b></source> + <translation><b>Remote-Prozess </b> %s wird ausgeführt auf <b>%s</b></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="490"/> + <source>is connecting to <b>%s</b> on %s port %d</source> + <translation>stellt eine Verbindung zu <b>%s</b> auf %s Port %d her</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="502"/> + <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source> + <translation>versucht <b>%s</b> über%s,%s Port%d aufzulösen</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="386"/> + <source>from this PID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/notifications.py" line="122"/> + <source>New outgoing connection</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="116"/> + <source>Reject</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="497"/> + <source>is connecting to <b>%s</b>, %s</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/notifications.py" line="42"/> + <source>Open</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>popups2</name> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="254"/> + <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source> + <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> + </message> +</context> +<context> + <name>preferences</name> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="171"/> + <source>Exception saving config: %s</source> + <translation type="obsolete">Fehler beim Speichern der Konfiguration: %s</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="177"/> + <source>Applying configuration on %s ...</source> + <translation type="obsolete">Konfiguration in %s anwenden ...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="292"/> + <source>Server address can not be empty</source> + <translation>Die Serveradresse darf nicht leer sein</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="227"/> + <source>Error loading %s configuration</source> + <translation type="obsolete">Fehler beim Laden der Konfiguration %s</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="568"/> + <source>Configuration applied.</source> + <translation>Konfiguration angewendet.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="257"/> + <source>Error applying configuration: %s</source> + <translation type="obsolete">Fehler beim Anwenden der Konfiguration: %s</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="386"/> + <source>Exception saving config: {0}</source> + <translation>Fehler beim Speichern der Konfiguration: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="500"/> + <source>Applying configuration on {0} ...</source> + <translation>Konfiguration in {0} anwenden ...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="321"/> + <source>Error loading {0} configuration</source> + <translation>Fehler beim Laden der Konfiguration {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="570"/> + <source>Error applying configuration: {0}</source> + <translation>Fehler beim Anwenden der Konfiguration: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> + <source>Warning</source> + <translation>Warnung</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> + <source>You must select a file for the database<br>or choose "In memory" type.</source> + <translation>Sie müssen eine Datei für die Datenbank auswählen<br>oder wählen Sie den Typ "Im Speicher".</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="401"/> + <source>DB type changed</source> + <translation>DB-Typ geändert</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="38"/> + <source>Restart the GUI in order effects to take effect</source> + <translation>Starten Sie die GUI neu, damit die Effekte wirksam werden</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="607"/> + <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source> + <translation>Fahren Sie mit der Maus über die Texte, um die Hilfe anzuzeigen<br><br>Vergessen Sie nicht, das Wiki zu besuchen: <a href="{0}">{0}</a></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="466"/> + <source>System</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="187"/> + <source>Themes not available. Install qt-material: pip3 install qt-material</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> + <source>UI theme changed</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> + <source>Restart the GUI in order to apply the new theme</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="508"/> + <source>Ok</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="388"/> + <source>There're no nodes connected</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="520"/> + <source>Exception saving node config {0}: {1}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="164"/> + <source>System default</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="433"/> + <source>Language changed</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>proc_details</name> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="100"/> + <source><b>Error loading process information:</b> <br><br> + +</source> + <translation><b>Fehler beim Laden der Prozessinformationen:</b> <br><br> + +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="119"/> + <source><b>Error stopping monitoring process:</b><br><br></source> + <translation><b>Fehler beim Beenden des Überwachungsprozesses:</b><br><br></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="159"/> + <source>loading...</source> + <translation>Wird geladen...</translation> + </message> +</context> +<context> + <name>rules</name> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="228"/> + <source>There're no nodes connected.</source> + <translation>Es sind keine Knoten verbunden.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="271"/> + <source>Rule applied.</source> + <translation>Regel angewendet.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="123"/> + <source>Error applying rule: %s</source> + <translation type="obsolete">Fehler beim Anwenden der Regel:%s</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="641"/> + <source>protocol can not be empty, or uncheck it</source> + <translation>Das Protokoll darf nicht leer sein oder die Option deaktivieren</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="655"/> + <source>Protocol regexp error</source> + <translation>Protokoll-Regexp-Fehler</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="659"/> + <source>process path can not be empty</source> + <translation>Prozesspfad darf nicht leer sein</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="673"/> + <source>Process path regexp error</source> + <translation>Prozesspfad-Regexp-Fehler</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="677"/> + <source>command line can not be empty</source> + <translation>Befehlszeile darf nicht leer sein oder die Option deaktivieren</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="691"/> + <source>Command line regexp error</source> + <translation>Befehlszeilen-Regexp-Fehler</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="731"/> + <source>Dest port can not be empty</source> + <translation>Der Zielport darf nicht leer sein oder die Option deaktivieren</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="745"/> + <source>Dst port regexp error</source> + <translation>Fehler im regulären Ausdruck des Zielports</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="749"/> + <source>Dest host can not be empty</source> + <translation>Der Zielhost kann nicht leer sein oder die Option deaktivieren</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="763"/> + <source>Dst host regexp error</source> + <translation>Fehler beim regulären Ausdruck des Zielhosts</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="805"/> + <source>Dest IP/Network can not be empty</source> + <translation>Ziel-IP / Netzwerk darf nicht leer sein</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="831"/> + <source>Dst IP regexp error</source> + <translation>Fehler beim regulären Ausdruck der Ziel-IP</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="843"/> + <source>User ID can not be empty</source> + <translation>Die Benutzer-ID darf nicht leer sein oder die Option deaktivieren</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="857"/> + <source>User ID regexp error</source> + <translation>Regexp-Fehler der Benutzer-ID</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="273"/> + <source>Error applying rule: {0}</source> + <translation>Fehler beim Anwenden der Regel: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="539"/> + <source><b>Error loading rule</b></source> + <translation><b>Fehler beim Laden der Regel</b></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="931"/> + <source>Lists field cannot be empty</source> + <translation>Listenfeld darf nicht leer sein</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="933"/> + <source>Lists field must be a directory</source> + <translation>Listenfeld muss ein Verzeichnis sein</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="976"/> + <source><b>Rule not supported</b></source> + <translation><b>Regel nicht unterstützt</b></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="245"/> + <source>There's already a rule with this name.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="861"/> + <source>PID field can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="875"/> + <source>PID field regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="963"/> + <source>Select at least one field.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="695"/> + <source>Network interface can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="709"/> + <source>Network interface regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="713"/> + <source>Source port can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="727"/> + <source>Source port regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="767"/> + <source>Source IP/Network can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="793"/> + <source>Source IP regexp error</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>stats</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="313"/> + <source>Not running</source> + <translation>Gestoppt</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="314"/> + <source>Disabled</source> + <translation>Deaktiviert</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="315"/> + <source>Running</source> + <translation>Eingeschaltet</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="412"/> + <source>OpenSnitch Network Statistics</source> + <translation type="obsolete">Eventos de OpenSnitch</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="414"/> + <source>OpenSnitch Network Statistics for</source> + <translation type="obsolete">Eventos de OpenSnitch de</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1189"/> + <source> Your are about to delete this rule. </source> + <translation> Sie sind im Begriff, diese Regel zu löschen. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> + <source> Are you sure?</source> + <translation> Bist du sicher?</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="636"/> + <source>OpenSnitch Network Statistics {0}</source> + <translation>OpenSnitch-Netzwerkstatistiken {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="638"/> + <source>OpenSnitch Network Statistics for {0}</source> + <translation>OpenSnitch-Netzwerkstatistiken für {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> + <source>Name</source> + <translation type="obsolete">Name</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Address</source> + <translation type="obsolete">Adresse</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="176"/> + <source>Status</source> + <translation type="obsolete">Status</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="177"/> + <source>Hostname</source> + <translation type="obsolete">Hostname</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="183"/> + <source>Version</source> + <translation type="obsolete">Version</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="834"/> + <source>Rules</source> + <translation type="unfinished">Regeln</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Time</source> + <translation type="obsolete">Zeit</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="875"/> + <source>Action</source> + <translation type="unfinished">Aktion</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> + <source>Duration</source> + <translation type="obsolete">Dauer</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> + <source>Node</source> + <translation type="obsolete">Knoten</translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="18"/> + <source>Hits</source> + <translation type="unfinished">Treffer</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>Protocol</source> + <translation type="obsolete">Protokoll</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2581"/> + <source>Save as CSV</source> + <translation>Als CSV speichern</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Enabled</source> + <translation type="obsolete">Aktiviert</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="961"/> + <source>Delete</source> + <translation>Löschen</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="948"/> + <source>always</source> + <translation type="obsolete">siempre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="575"/> + <source><b>Error:</b><br><br>{0}</source> + <translation type="obsolete"><b>Error:</b><br><br>{0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1301"/> + <source><b>Error:</b><br><br></source> + <comment>{0}</comment> + <translation><b>Fehler:</b><br><br></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1308"/> + <source>Warning:</source> + <translation>Warnung:</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="940"/> + <source>Allow</source> + <translation>Erlauben</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="941"/> + <source>Deny</source> + <translation>Verweigern</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="945"/> + <source>Always</source> + <translation>Immer</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="946"/> + <source>Until reboot</source> + <translation>Bis zum Neustart</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="954"/> + <source>Disable</source> + <translation>Deaktivieren</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="956"/> + <source>Enable</source> + <translation>Aktivieren</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="959"/> + <source>Duplicate</source> + <translation>Duplizieren</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="960"/> + <source>Edit</source> + <translation>Bearbeiten</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1248"/> + <source>Rule not found by that name and node</source> + <translation>Regel von diesem Namen und Knoten nicht gefunden</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> + <source> You are about to delete this rule. </source> + <translation> Sie sind dabei, diese Regel zu löschen. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> + <source>Rule</source> + <translation type="obsolete">Regel</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> + <source>Name</source> + <comment>xxxxx</comment> + <translation type="obsolete">Name</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> + <source>Name</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Name</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Address</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Adresse</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> + <source>Status</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Status</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> + <source>Hostname</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Hostname</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> + <source>Version</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Version</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> + <source>Rules</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Regeln</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Time</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Zeit</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> + <source>Action</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Aktion</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> + <source>Duration</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Dauer</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> + <source>Node</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Knoten</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Enabled</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Aktiviert</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> + <source>Hits</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Treffer</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>Protocol</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Protokoll</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> + <source>Rule</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Regel</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="287"/> + <source>Name</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Name</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="288"/> + <source>Address</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Adresse</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="289"/> + <source>Status</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Status</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="290"/> + <source>Hostname</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Hostname</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="423"/> + <source>Version</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Version</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="420"/> + <source>Rules</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Regeln</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Time</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Zeit</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> + <source>Action</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Aktion</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> + <source>Duration</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Dauer</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> + <source>Node</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Knoten</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Enabled</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Aktiviert</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="438"/> + <source>Hits</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Treffer</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> + <source>Protocol</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Protokoll</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Process</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Prozess</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>Destination</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Ziel</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> + <source>Rule</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Regel</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> + <source>UserID</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>BenutzerID</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> + <source>LastConnection</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>LetzteVerbindung</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> + <source>DstIP</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>ZielIP</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> + <source>DstHost</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>ZielHost</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> + <source>DstPort</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>ZielPort</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="174"/> + <source>LastConnection</source> + <translation type="obsolete">LetzteVerbindung</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="179"/> + <source>Uptime</source> + <translation type="obsolete">Betriebszeit</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="181"/> + <source>Connections</source> + <translation type="obsolete">Verbindungen</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="182"/> + <source>Dropped</source> + <translation type="obsolete">Abgelehnt</translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="17"/> + <source>What</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="931"/> + <source>Apply to</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="942"/> + <source>Reject</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="19"/> + <source>Network name</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="291"/> + <source>Uptime</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished">Betriebszeit</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="421"/> + <source>Connections</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished">Verbindungen</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="422"/> + <source>Dropped</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished">Abgelehnt</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="437"/> + <source>What</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> + <source>Precedence</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="776"/> + <source>New node connected</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> + <source>Description</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> + <source>Cmdline</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="406"/> + <source>Export rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="407"/> + <source>Import rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="408"/> + <source>Export events to CSV</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> + <source>Quit</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="932"/> + <source>Export</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="964"/> + <source>To clipboard</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="965"/> + <source>To disk</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2523"/> + <source>Select a directory to export rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1191"/> + <source> Your are about to delete this entry. </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1678"/> + <source> You are about to delete this node. </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1687"/> + <source><b>Error deleting node</b><br><br></source> + <comment>{0}</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2478"/> + <source>Error exporting rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2552"/> + <source>Select a directory with rules to import (JSON files)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2566"/> + <source>Rules imported fine</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="211"/> + <source>WARNING</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="833"/> + <source>Details</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="835"/> + <source>New</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>stats_deleterule</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="774"/> + <source> Your are about to delete this rule. </source> + <translation type="obsolete"> Estás a punto de borrar esta regla. </translation> + </message> +</context> +<context> + <name>stats_deleterule2</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="776"/> + <source> Are you sure?</source> + <translation type="obsolete"> ¿Estás seguro?</translation> + </message> +</context> +<context> + <name>stats_disabled</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="74"/> + <source>Disabled</source> + <translation type="obsolete">Deshabilitado</translation> + </message> +</context> +<context> + <name>stats_notrunning</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="73"/> + <source>Not running</source> + <translation type="obsolete">Parado</translation> + </message> +</context> +<context> + <name>stats_running</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="75"/> + <source>Running</source> + <translation type="obsolete">Interceptando</translation> + </message> +</context> +<context> + <name>stats_wintitle</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> + <source>OpenSnitch Network Statistics</source> + <translation type="obsolete">Eventos de red OpenSnitch</translation> + </message> +</context> +<context> + <name>stats_wintitle2</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="411"/> + <source>OpenSnitch Network Statistics for</source> + <translation type="obsolete">Eventos de OpenSnitch de</translation> + </message> +</context> +</TS> diff --git a/ui/i18n/locales/es_ES/opensnitch-es_ES.ts b/ui/i18n/locales/es_ES/opensnitch-es_ES.ts new file mode 100644 index 0000000..8572881 --- /dev/null +++ b/ui/i18n/locales/es_ES/opensnitch-es_ES.ts @@ -0,0 +1,3301 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="es_ES"> +<context> + <name>Dialog</name> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="34"/> + <source>opensnitch-qt</source> + <translation>opensnitch-qt</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="300"/> + <source>User ID</source> + <translation>UID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="334"/> + <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source> + <translation>Ejecutado desde</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="647"/> + <source>TextLabel</source> + <translation>Etiqueta de texto</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="437"/> + <source>Source IP</source> + <translation>IP origen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="458"/> + <source>Process ID</source> + <translation>PID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="601"/> + <source>Destination IP</source> + <translation>IP destino</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="622"/> + <source>Dst Port</source> + <translation>Puerto destino</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="702"/> + <source>from this executable</source> + <translation>de este ejecutable</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="707"/> + <source>from this command line</source> + <translation>de este comando</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="712"/> + <source>this destination port</source> + <translation>este puerto destino</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="717"/> + <source>this user</source> + <translation>este usuario</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="722"/> + <source>this destination ip</source> + <translation>esta IP destino</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="751"/> + <source>once</source> + <translation>una vez</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="706"/> + <source>for this session</source> + <translation type="obsolete">durante esta sesión</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="786"/> + <source>forever</source> + <translation>para siempre</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="346"/> + <source>Deny</source> + <translation type="unfinished">Denegar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="337"/> + <source>Allow</source> + <translation>Permitir</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="865"/> + <source>+</source> + <translation>+</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="781"/> + <source>until reboot</source> + <translation>Hasta reiniciar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="727"/> + <source>from this PID</source> + <translation>a partir de este PID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="809"/> + <source>action</source> + <translation>acción</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="756"/> + <source>30s</source> + <translation>30 segundos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="761"/> + <source>5m</source> + <translation>5 minutos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="766"/> + <source>15m</source> + <translation>15 minutos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="771"/> + <source>30m</source> + <translation>30 minutos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="776"/> + <source>1h</source> + <translation>1 hora</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="14"/> + <source>Firewall</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="55"/> + <source><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="320"/> + <source>Inbound</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="313"/> + <source>Outbound</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="275"/> + <source>Profile</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="375"/> + <source>Allow inbound connections to a port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="378"/> + <source>Allow service (IN)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="397"/> + <source>Exclude outbound connections to a port from being intercepted</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="406"/> + <source>Allow service (OUT)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="426"/> + <source>New rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="431"/> + <source>Close</source> + <translation type="unfinished">Cerrar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="14"/> + <source>Firewall rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="26"/> + <source>Node</source> + <translation type="unfinished">Nodo</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="38"/> + <source>Enable</source> + <translation type="unfinished">Habilitar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="50"/> + <source>Description</source> + <translation type="unfinished">Descripción</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="90"/> + <source>Simple</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="154"/> + <source>Add new condition</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="177"/> + <source>Remove selected condition</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="233"/> + <source>Direction</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="248"/> + <source>IN</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="257"/> + <source>OUT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="223"/> + <source>Action</source> + <translation type="unfinished">Acción</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="285"/> + <source>ACCEPT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="294"/> + <source>DROP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="303"/> + <source>REJECT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="312"/> + <source>RETURN</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="442"/> + <source>Clear</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="453"/> + <source>Delete</source> + <translation type="unfinished">Eliminar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="464"/> + <source>Save</source> + <translation type="unfinished">Guardar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="475"/> + <source>Add</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="266"/> + <source>FORWARD</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="271"/> + <source>PREROUTING</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="276"/> + <source>POSTROUTING</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="321"/> + <source>QUEUE</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="330"/> + <source>DNAT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="335"/> + <source>SNAT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="340"/> + <source>REDIRECT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="359"/> + <source>depending on the Action (i.e.: target), the syntaxis of the parameters will vary. +Some examples: + +QUEUE -> num 0 (or 1, 2, ...) +REDIRECT, TPROXY, DNAT, SNAT, MASQUERADE: + to :22 + to 192.168.1.254:8080 + to 192.168.1.254 + to 1024-2048 (masquerade)</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>PreferencesDialog</name> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="14"/> + <source>Preferences</source> + <translation>Preferencias</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="484"/> + <source>UI</source> + <translation>UI</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="54"/> + <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p></body></html></source> + <translation type="obsolete">Este timeout es la cuenta atrás que aparece cuando se muestra una ventana emergente</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="466"/> + <source>Default timeout</source> + <translation>Timeout por defecto</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="340"/> + <source>Pop-up default duration</source> + <translation>Duración por defecto (de la acción/regla)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="866"/> + <source>Default duration</source> + <translation>Duración por defecto</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="162"/> + <source>Pop-up default action</source> + <translation type="obsolete">Acción por defecto de la ventana emergente</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="483"/> + <source>Default action</source> + <translation type="obsolete">Acción por defecto</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="323"/> + <source>Default target</source> + <translation>Filtrado por defecto</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="179"/> + <source>center</source> + <translation>centro</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="184"/> + <source>top right</source> + <translation>Arriba a la derecha</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="189"/> + <source>bottom right</source> + <translation>Abajo a la derecha</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="194"/> + <source>top left</source> + <translation>Arriba a la izquierda</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="199"/> + <source>bottom left</source> + <translation>Abajo a la izquierda</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="167"/> + <source>Prompt dialog default position on screen</source> + <translation type="obsolete">Posición por defecto</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="283"/> + <source>by executable</source> + <translation>por ejecutable</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="288"/> + <source>by command line</source> + <translation>por comando</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="293"/> + <source>by destination port</source> + <translation>por puerto destino</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="298"/> + <source>by destination ip</source> + <translation>por IP destino</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="303"/> + <source>by user id</source> + <translation>por UID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="970"/> + <source>once</source> + <translation>una vez</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="240"/> + <source>for this session</source> + <translation type="obsolete">durante esta sesión</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="249"/> + <source>forever</source> + <translation>para siempre</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1012"/> + <source>deny</source> + <translation>Denegar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1021"/> + <source>allow</source> + <translation>Permitir</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="406"/> + <source>Disable pop-ups, only display an alert</source> + <translation type="obsolete">Deshabilitar ventanas emergentes, +sólo mostrar alerta</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="823"/> + <source>Nodes</source> + <translation>Nodos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="829"/> + <source>Process monitor method</source> + <translation>Método parar monitorizar procesos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="863"/> + <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source> + <translation>La Duración por defecto se aplicará cuando no haya ninguna UI conectada</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="988"/> + <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source> + <translation><html><head/><body><p>Dirección del nodo.</p><p>Por defecto: unix:///tmp/osui.sock (unix:// es obligatorio si es un socket Unix)</p><p>También puede ser una IP con este formato: 127.0.0.1:50051, 192.168.1.122:12345, etc..</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="991"/> + <source>Address</source> + <translation>Dirección</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1131"/> + <source>Default log level</source> + <translation>Nivel de log por defecto</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1039"/> + <source>Version</source> + <translation>Versión</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="902"/> + <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source> + <translation>La Acción por defecto se aplicará cuando no haya ninguna UI conectada</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="846"/> + <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source> + <translation><html><head/><body><p>Fichero en el que escribir los logs.<br/></p><p>/dev/stdout escribirá los logs por la salida estándar del servicio.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="849"/> + <source>Log file</source> + <translation>Fichero de log</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="578"/> + <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons.</p><p>The pop-up dialog will only contain information about the network connection.</p></body></html></source> + <translation type="obsolete">Si marcas esta opción, OpenSnitch te preguntará para Aceptar o Denegar conexiones que no tengan un PID asociado por diferentes razones. + +La ventana emergente sólo contendrá información relativa a la conexión. + +Nota: Estas conexiones no tienen por qué indicar que algo sospechoso está sucediendo. Simplemente +es que no hemos descubierto el PID (por ejemplo conexiones que no se originan en la máquina, o paquetes en mal estado).</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="581"/> + <source>Intercept Unknown Connections</source> + <translation type="obsolete">Interceptar conexiones desconocidas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="921"/> + <source>HostName</source> + <translation>Nombre del host</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1090"/> + <source>unix:///tmp/osui.sock</source> + <translation>unix:///tmp/osui.sock</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="975"/> + <source>until restart</source> + <translation>hasta reiniciar (el servicio)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="980"/> + <source>always</source> + <translation>siempre</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1102"/> + <source>/var/log/opensnitchd.log</source> + <translation>/var/log/opensnitchd.log</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1107"/> + <source>/dev/stdout</source> + <translation>/dev/stdout</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="879"/> + <source>Apply configuration to all nodes</source> + <translation>Aplicar configuración a todos +los nodos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1146"/> + <source>Database</source> + <translation>Datos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1181"/> + <source>In memory</source> + <translation>En memoria</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1186"/> + <source>File</source> + <translation>Fichero</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1482"/> + <source>Close</source> + <translation>Cerrar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1493"/> + <source>Apply</source> + <translation>Aplicar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1504"/> + <source>Save</source> + <translation>Guardar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="244"/> + <source>until reboot</source> + <translation>Hasta reiniciar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1200"/> + <source>Database type</source> + <translation>Tipo de base de datos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1207"/> + <source>Select</source> + <translation>Seleccionar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="83"/> + <source>Pop-ups default options</source> + <translation type="obsolete">Opciones por defecto de las ventanas emergentes</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="367"/> + <source>Pop-ups default position on screen</source> + <translation type="obsolete">Posición en pantalla</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="102"/> + <source><html><head/><body><p>The advanced view allows you to apply more filters on a connection</p><p>when a pop-up appears.</p></body></html></source> + <translation type="obsolete">La vista avanzada permite filtrar conexiones por más parámetros</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="359"/> + <source>Show advanced view by default</source> + <translation>Mostrar vista avanzada por defecto</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="653"/> + <source>Action</source> + <translation>Acción</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="375"/> + <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source> + <translation>Si se selecciona, las ventanas emergentes se mostrarán con la vista avanzada activada</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="343"/> + <source>Duration</source> + <translation>Duración</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="263"/> + <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source> + <translation>Por defecto cuando una ventana emergente aparece, en su forma más simple, puedes filtrar conexiones por un +parámetro de la conexión (ejecutable, puerto, IP, etc). + +Con estas opciones, puedes seleccionar varios campos por los que filtrar por defecto conexiones</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="266"/> + <source>Filter connections also by:</source> + <translation>Filtrar conexiones también por:</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="362"/> + <source>If checked, this field will be checked when a pop-up is displayed</source> + <translation type="obsolete">Si lo seleccionas, este campo se usará para filtrar las conexiones</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="81"/> + <source>User ID</source> + <translation>UID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="97"/> + <source>Destination port</source> + <translation>Puerto destino</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="113"/> + <source>Destination IP</source> + <translation>IP destino</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="463"/> + <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source> + <translation>Este timeout es la cuenta atrás que ves cuando se muestra una ventana emergente + +Si no respondes a la ventana emergente, se aplicarán las opciones por defecto</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="356"/> + <source>The advanced view allows you to easily select multiple fields to filter connections</source> + <translation>La vista avanzada te permite seleccionar fácilmente múltiples campos para filtrar conexiones</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="110"/> + <source>If checked, this field will be selected when a pop-up is displayed</source> + <translation>Si se selecciona, este campo estará marcado cuando una ventana emergente aparezca</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="159"/> + <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source> + <translation><html><head/><body><p>Acción por defecto de la ventana emergente.</p><p>Cuando una nueva conexión saliente está a punto de establecerse, esta acción será la predeterminada, por lo que si llega el timeout, está será la que se aplique.</p><p><br/></p><p>Mientras una ventana emergente está activa esperando ser aprobada o denegada:</p><p>1. Las nuevas conexiones salientes son denegadas (según la configuración del demonio)</p><p>2. Las conexiones ya conocidas se permitirán o denegarán en base a las reglas ya creadas por el usuario.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="905"/> + <source>Default action when the GUI is disconnected</source> + <translation>Opción por defecto cuando la GUI no está conectada</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1001"/> + <source>Debug invalid connections</source> + <translation>Depurar conexiones inválidas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="39"/> + <source>Pop-ups</source> + <translation>Ventanas emergentes</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="64"/> + <source>Default options</source> + <translation>Opciones por defecto</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="330"/> + <source>Default position on screen</source> + <translation>Posición por defecto en la pantalla</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="769"/> + <source>any temporary rules</source> + <translation>cualquier regla temporal</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="487"/> + <source><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Cuando esta opción está seleccionada, las reglas de la duración elegida no se añadirán a la lista de reglas temporales en la GUI.</p><p><br/></p><p>Las reglas temporales seguirán siendo válidas, y puedes usarlas cuando se pregunte para permitir o denegar una nueva conexión.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="490"/> + <source>Don't save rules of duration</source> + <translation type="obsolete">No guardar reglas de duración</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="463"/> + <source>Show events columns</source> + <translation type="obsolete">Mostrar columnas de la pestaña Eventos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="589"/> + <source>Time</source> + <translation>Hora</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="669"/> + <source>Destination</source> + <translation>Destino</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="637"/> + <source>Protocol</source> + <translation>Protocolo</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="685"/> + <source>Process</source> + <translation>Aplicación</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="605"/> + <source>Rule</source> + <translation>Regla</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="621"/> + <source>Node</source> + <translation>Nodo</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="723"/> + <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using wireguard.</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Si se selecciona opensnitch te preguntará para permitir o denegar conexiones que no tienen un PID asociado. Esto puede pasar por diferentes motivos, principalmente debido a conexiones inválidas.</p><p>La ventana emergente sólo contendrá información de la conexión.</p><p>Hay algunas situaciones en las que estas conexiones son válidas, por ejemplo al establecer un túnel VPN con wireguard.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="577"/> + <source>Events tab columns</source> + <translation>Columnas de la pestaña Eventos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="308"/> + <source>by PID</source> + <translation>por PID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="473"/> + <source>Disable pop-ups, only display a notification</source> + <translation>Deshabilitar ventanas emergentes, sólo mostrar notificaciones</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="496"/> + <source>Desktop notifications</source> + <translation>Notificaciones de escritorio</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="514"/> + <source>Use system notifications</source> + <translation>Usar notificaciones del sistema</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="530"/> + <source>Use Qt notifications</source> + <translation>Usar notificaciones de Qt</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="559"/> + <source>Test</source> + <translation>Probar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="998"/> + <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source> + <translation><html><head/><body><p>Si lo marcas, OpenSnitch sólo te preguntará para denegar o permitir conexiones que por diversas razones no tengan un PID/aplicación asociado. Generalmente son conexiones en estado erróneo.</p><p>La ventana emergente sólo contendrá información sobre la conexión de red.</p><p>Hay algunos casos en los que estas conexiones pueden ser válidas, como cuando se establecen conexiones VPN.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1294"/> + <source>minutes</source> + <translation>minutos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1326"/> + <source>Minutes between events purges</source> + <translation>Minutos entre borrado de eventos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1352"/> + <source>days</source> + <translation>días</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1365"/> + <source>Maximum days of events to keep</source> + <translation>Máximo de días a guardar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="147"/> + <source>reject</source> + <translation>rechazar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="716"/> + <source>System</source> + <translation>Sistema</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="695"/> + <source>Command line</source> + <translation>Línea de comando</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="708"/> + <source>Theme</source> + <translation>Tema</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="219"/> + <source>30s</source> + <translation>30 segundos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="224"/> + <source>5m</source> + <translation>5 minutos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="229"/> + <source>15m</source> + <translation>15 minutos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="234"/> + <source>30m</source> + <translation>30 minutos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="239"/> + <source>1h</source> + <translation>1 hora</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="748"/> + <source>Rules</source> + <translation type="unfinished">Reglas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="756"/> + <source>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI. + +Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="761"/> + <source>Don't save/Delete rules of duration</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="779"/> + <source>30s or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="784"/> + <source>5m or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="789"/> + <source>15m or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="794"/> + <source>30m or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="799"/> + <source>1h or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="724"/> + <source>Language</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>ProcessDetailsDialog</name> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="14"/> + <source>Process details</source> + <translation>Detalles del proceso</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="61"/> + <source>loading...</source> + <translation>cargando...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="81"/> + <source>CWD: loading...</source> + <translation>CWD: cargando...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="93"/> + <source>mem stats: loading...</source> + <translation>estadísticas de memoria: cargando...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="121"/> + <source>Status</source> + <translation>Estado</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="135"/> + <source>Open files</source> + <translation>Ficheros abiertos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="149"/> + <source>I/O Statistics</source> + <translation>Estadísticas Entrada/Salida</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="163"/> + <source>Memory mapped files</source> + <translation>Ficheros cargados en memoria</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="177"/> + <source>Stack</source> + <translation>Pila</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="191"/> + <source>Environment variables</source> + <translation>Variables de entorno</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="210"/> + <source>Application pids</source> + <translation>PIDs de la aplicación</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="240"/> + <source>Start or stop monitoring this process</source> + <translation>Iniciar o Parar el monitorizado de este proceso</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="256"/> + <source>Close</source> + <translation>Cerrar</translation> + </message> +</context> +<context> + <name>RulesDialog</name> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="20"/> + <source>Rule</source> + <translation>Regla</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> + <source>Node</source> + <translation>Nodo</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> + <source>Apply rule to all nodes</source> + <translation>Aplicar regla a todos los nodos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="379"/> + <source>From this command line</source> + <translation>De este comando</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="472"/> + <source>From this executable</source> + <translation>De este ejecutable</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="56"/> + <source>Action</source> + <translation>Acción</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="138"/> + <source>/path/to/executable, .*/bin/executable[0-9\.]+$, ...</source> + <translation type="obsolete">/ruta/al/ejecutable, .*/bin/executable[0-9\.]+$, ...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="610"/> + <source>To this IP / Network</source> + <translation>A esta IP/Red</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="97"/> + <source>once</source> + <translation>una vez</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="230"/> + <source>until restart</source> + <translation type="obsolete">hasta reiniciar (el servicio)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="132"/> + <source>always</source> + <translation>siempre</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="902"/> + <source>To this port</source> + <translation>A este puerto</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="372"/> + <source>From this user ID</source> + <translation>De este UID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/> + <source>Commas or spaces are not allowed to specify multiple domains. + +Use regular expressions instead: +.*(opensnitch|duckduckgo).com +.*\.google.com + +or a single domain: +www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... +gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source> + <translation>No se permiten ni comas ni espacios para especificar múltiples dominios. + +Puedes usar expresiones regulares en su lugar: + +.*(opensnitch|duckduckgo).com +.*\.google.com + +o un único dominio: +www.gnu.org - sólo filtrará www.gnu.org, NO filtrará ftp.gnu.org ni www2.gnu.org +gnu.org - sólo filtrará gnu.org, ni www.gnu.org, ni ftp. gnu.org, ...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="603"/> + <source>www.domain.org, .*\.domain.org</source> + <translation>www.dominio.org, .*\.dominio.org</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="526"/> + <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source> + <translation>Sólo se permiten las opciones TCP, UDP o UDPLITE. Puedes usar expresiones regulares +sobre estas opciones, por ejemplo TCP o UDP: ^(TCP|UDP)$</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="532"/> + <source>TCP</source> + <translation>TCP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="760"/> + <source>You can specify a single IP: +- 192.168.1.1 + +or a regular expression: +- 192\.168\.1\.[0-9]+ + +multiple IPs: +- ^(192\.168\.1\.1|172\.16\.0\.1)$ + +You can also specify a subnet: +- 192.168.1.0/24 + +Note: Commas or spaces are not allowed to separate IPs or networks.</source> + <translation>Puedes especificar una IP: +- 192.168.1.1 + +o una expresión regular: +- 192\.168\.1\.[0-9]+ + +múltiples IPs: +- ^(192\.168\.1\.1|172\.16\.0\.1)$ + +También puedes especificar una subnet: +- 192.168.1.0/24 + +Nota: No se permiten ni comas ni espacios para especificar IPs o redes.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="89"/> + <source>Duration</source> + <translation>Duración</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="633"/> + <source>Protocol</source> + <translation>Protocolo</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="750"/> + <source>To this host</source> + <translation>A este host</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="151"/> + <source>Deny</source> + <translation>Denegar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="191"/> + <source>Allow</source> + <translation>Permitir</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="245"/> + <source>Name</source> + <translation type="unfinished">Nombre</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="207"/> + <source>Enable</source> + <translation>Habilitar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="238"/> + <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them. + +000-allow-localhost +001-deny-broadcast +...</source> + <translation>Las reglas se comprueban en orden alfabético, por lo que debes nombrarlas así para priorizarlas. + +000-allow-localhost +0001-deny-broadcast +...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="773"/> + <source>leave blank to autocreate</source> + <translation type="obsolete">dejar en blanco para autoasignar nombre</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="214"/> + <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one. + +You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example: + +[x] Priority - 000-priority-rule +[ ] Priority - 001-less-priority-rule</source> + <translation>Si marcas esta opción, esta regla tendrá prioridad sobre el resto de reglas cuando le toque evaluarla. No se comprobará ninguna regla más después de esta si coincide. + +Debes nombrar la regla de tal manera que se compruebe de las primeras, ya que se nombran alfabéticamente. Por ejemplo: + +[x] Prioritaria-000-regla-prioritaria +[ ] Prioritaria-001-regla-menos-prioritaria</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="222"/> + <source>Priority rule</source> + <translation>Prioritaria</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1092"/> + <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source> + <translation><html><head/><body><p>Por defecto los campos de una regla NO son sensibles a mayúsculas/minúsculas, es decir, si un proceso trata de acceder a gOOgle.CoM y tienes una regla para Denegar .*google.com, la conexión será bloqueada. <br/></p><p>Si marcas esta opción y quieres bloquear EXACTAMENTE gOOgle.CoM, tendrás que especificar en el campo de la regla el dominio exacto que quieres filtrar (en este caso: gOOgle.CoM).</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1095"/> + <source>Case-sensitive</source> + <translation>Distinguir mayúsculas/minúsculas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="686"/> + <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> + <translation type="obsolete">Puedes especificar múltiples puertos usando expresiones regulares: + +- 53, 80 o 443: +^(53|80|443)$ + +- 53, 443 o 5551, 5552, 5553, etc: +^(53|443|555[0-9])$</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="127"/> + <source>until reboot</source> + <translation>Hasta reiniciar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="980"/> + <source>To this list of domains</source> + <translation>A esta lista de dominios</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="539"/> + <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Selecciona un directorio con listas de dominios para permitir o denegar.</p><p>Mete dentro de este directorio ficheros con cualquier extensión que contengan listas de dominios.</p><p><br/>El formato de cada dominio de la lista tiene que estar en formato hosts, así:</p><p>127.0.0.1 www.domain.com</p><p>o </p><p>0.0.0.0 www.domain.com</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="346"/> + <source>Applications</source> + <translation>Aplicaciones</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="216"/> + <source><html><head/><body><p>This field will only match the executable path. It is not modifiable by the user.<br/></p><p>You can use regular expressions to deny executions from /tmp for example:<br/></p><p>^/tmp/.*$</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Este campo sólo comprueba la ruta del ejecutable (la cual no es modificable por el usuario).<br/></p><p>Puedes usar expresiones regulares para denegar cualquier ejecución desde /tmp, por ejemplo; ^/tmp/.*$</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="389"/> + <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source> + <translation><html><head/><body><p>Este campo contendrá y comprobará solamente la linea de comandos tecleada por el usuario.<br/></p><p>Si el usuario sólo escribió el comando (sin la ruta absoluta), sólo aparecerá el comando:</p><p>telnet 1.2.3.4<br/></p><p>Si el usuario escribió la ruta absoluta o relativa al comando, eso es lo que aparecerá:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="399"/> + <source>From this PID</source> + <translation>De este PID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="491"/> + <source>Network</source> + <translation>Red</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="932"/> + <source>List of domains/IPs</source> + <translation>A esta lista de dominios/IPs</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="938"/> + <source>To this list of network ranges</source> + <translation>A esta lista de rangos de red</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="945"/> + <source>To this list of IPs</source> + <translation>A esta lista de IPs</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="971"/> + <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation><html><head/><body><p>Selecciona un directorio con ficheros que contengan listas de IPs a bloquear o permitir:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>Una IP por linea. Las lineas en blanco o que comiencen con # serán ignoradas.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1006"/> + <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation><html><head/><body><p>Selecciona un directorio con ficheros que contengan listas de rangos de red a bloquear o permitir:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>Un rango de red por linea. Las lineas en blanco o que comiencen con # serán ignoradas.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1034"/> + <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source> + <translation><html><head/><body><p>Selecciona un directorio con ficheros que contengan listas de dominios a bloquear o permitir.</p><p>Los ficheros de ese directorio pueden tener cualquier extensión.</p><p><br/>El formato de cada linea es como sigue (formato hosts):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Las lineas en blanco o que comiencen con # serán ignoradas.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1049"/> + <source>To this list of domains +(regular expressions)</source> + <translation>A esta lista de dominios +(expresiones regulares)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1076"/> + <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation><html><head/><body><p>Selecciona un directorio con ficheros que contengan expresiones regulares de dominios para bloquear o permitir:</p><p>.*\.example\.com</p><p>También puedes usar un dominio literal (sin expresión regular): &quot;example.com&quot; ,comprobará whatever.example.com, whatever.example.com.localdomain, etc.</p><p>Un dominio por linea. Las lineas en blanco o que comiencen con # serán ignoradas</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="168"/> + <source>Reject</source> + <translation>Rechazar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1151"/> + <source>Description...</source> + <translation>Descripción...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="355"/> + <source><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></source> + <translation><html><head/><body><p>El valor de este campo es siempre la ruta absoluta al ejecutable: /path/to/binary<br/></p><p>Ejemplos:</p><p>- Sencillo: /path/to/binary</p><p>- Múltiples caminos: ^/usr/lib(64|)/firefox/firefox$</p><p>- Binarios múltiples: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Denegar/permitir ejecuciones de /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>Para ver más ejemplos, visite <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">página en la wiki</a> o pregunte en los <a href="https://github.com/evilsocket/opensnitch/discussions">Foros de debate</a>.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="365"/> + <source>Is regular expression</source> + <translation>Es una expresión regular</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> + <source>is regular expression</source> + <translation>es una expresión regular</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="863"/> + <source>Network interface</source> + <translation>Interfaz de la red</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1086"/> + <source>More</source> + <translation>Más</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1102"/> + <source>Don't log connections that match this rule</source> + <translation>No registrar conexiones que coincidan con esta regla</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1105"/> + <source>Don't log connections</source> + <translation>No registrar conexiones</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="148"/> + <source>Deny will just discard the connection</source> + <translation>Denegar sólo descartará la conexión</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="165"/> + <source>Reject will drop the connection, and kill the socket that initiated it</source> + <translation>Si se rechaza, se cancelará la conexión y se eliminará el socket que la inició</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="185"/> + <source>Allow will allow the connection</source> + <translation>Permitir autorizará la conexión</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="566"/> + <source>ICMP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="571"/> + <source>ICMP6</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="576"/> + <source>SCTP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="581"/> + <source>SCTP6</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="743"/> + <source>From this IP / Network</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="872"/> + <source>From this port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="918"/> + <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>StatsDialog</name> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="34"/> + <source>OpenSnitch Network Statistics</source> + <translation>Eventos de red - OpenSnitch</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="287"/> + <source>Save to CSV</source> + <translation type="obsolete">Exportar a CSV.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="297"/> + <source>Ctrl+S</source> + <translation type="obsolete">Ctrl+S</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="333"/> + <source>Create a new rule</source> + <translation>Crear una nueva regla</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="376"/> + <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source> + <translation><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">Nombre del host - 192.168.1.1</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="413"/> + <source>Status</source> + <translation>Estado</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1793"/> + <source>-</source> + <translation>-</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="451"/> + <source>Start or Stop interception</source> + <translation>Parar o iniciar la interceptación</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="496"/> + <source>Events</source> + <translation>Eventos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="94"/> + <source>Filter</source> + <translation>Filtrar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="107"/> + <source>Allow</source> + <translation>Permitir</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="116"/> + <source>Deny</source> + <translation>Denegar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="143"/> + <source>Ex.: firefox</source> + <translation>Ejemplo: firefox</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="205"/> + <source>50</source> + <translation>50</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="210"/> + <source>100</source> + <translation>100</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="215"/> + <source>200</source> + <translation>200</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="220"/> + <source>300</source> + <translation>300</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="826"/> + <source>Nodes</source> + <translation>Nodos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="554"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Addr column to view details of a node)</span></p></body></html></source> + <translation type="obsolete">(doble click en la columna Dirección para ver los detalles)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1700"/> + <source>Rules</source> + <translation>Reglas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="995"/> + <source>enable</source> + <translation>habilitar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="684"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Name column to view details of a rule)</span></p></body></html></source> + <translation type="obsolete">(doble click en la columna Nombre para ver los detalles)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="692"/> + <source>search rule name</source> + <translation type="obsolete">buscar regla</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="782"/> + <source>Application rules</source> + <translation>Reglas de aplicación</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="936"/> + <source>Permanent</source> + <translation>Permanentes</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="945"/> + <source>Temporary</source> + <translation>Temporales</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1063"/> + <source>Hosts</source> + <translation>Dominios</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1364"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click to view details of an item)</span></p></body></html></source> + <translation type="obsolete">(doble click en un dominio para ver detalles)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1153"/> + <source>Applications</source> + <translation>Aplicaciones</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1266"/> + <source>Addresses</source> + <translation>Direcciones</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1356"/> + <source>Ports</source> + <translation>Puertos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1440"/> + <source>Users</source> + <translation>Usuarios</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1544"/> + <source>Connections</source> + <translation>Conexiones</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1596"/> + <source>Dropped</source> + <translation>Rechazadas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1648"/> + <source>Uptime</source> + <translation>Tiempo de ejecución</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1767"/> + <source>Version</source> + <translation>Versión</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="233"/> + <source>Delete all intercepted events</source> + <translation>Borrar todos los eventos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1022"/> + <source>Edit rule</source> + <translation>Editar regla</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1039"/> + <source>Delete rule</source> + <translation>Borrar regla</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="926"/> + <source>Delete all intercepted hosts</source> + <translation type="obsolete">Borrar todos los hosts</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1051"/> + <source>Delete all intercepted applications</source> + <translation type="obsolete">Borrar todos las aplicaciones</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1159"/> + <source>Delete all intercepted addresses</source> + <translation type="obsolete">Borrar todas las direcciones</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1261"/> + <source>Delete all intercepted ports</source> + <translation type="obsolete">Borrar todos los puertos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1371"/> + <source>Delete all intercepted users</source> + <translation type="obsolete">Borrar todos los usuarios</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="699"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click on a row to view details of a rule)</span></p></body></html></source> + <translation type="obsolete">(Doble click en una fila para editar una regla)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="912"/> + <source>Delete connections that matched this rule</source> + <translation type="obsolete">Borrar conexiones que coinciden con esta regla</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="927"/> + <source>All applications</source> + <translation>Todas las reglas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="125"/> + <source>Reject</source> + <translation>Rechazar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="180"/> + <source>0</source> + <translation>0</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="777"/> + <source>2</source> + <translation>2</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="954"/> + <source>System rules</source> + <translation>Normas del sistema</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="637"/> + <source>Delete this node</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="653"/> + <source>Show the preferences of this node</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="669"/> + <source>Start or stop interception of this node</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>contextual_menu</name> + <message> + <location filename="../../../opensnitch/service.py" line="47"/> + <source>Statistics</source> + <translation>Eventos</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="50"/> + <source>Help</source> + <translation>Ayuda</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="51"/> + <source>Close</source> + <translation>Cerrar</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="48"/> + <source>Enable</source> + <translation>Habilitar</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="49"/> + <source>Disable</source> + <translation>Deshabilitar</translation> + </message> +</context> +<context> + <name>firewall</name> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="91"/> + <source>Configuration applied.</source> + <translation type="unfinished">Configuración aplicada.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="404"/> + <source>Error: {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="193"/> + <source>Applying changes...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="230"/> + <source>Error getting INPUT chain policy</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="237"/> + <source>Error getting OUTPUT chain policy</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="290"/> + <source>In order to configure firewall rules from the GUI, we need to use 'nftables' instead of 'iptables'</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="304"/> + <source>Enabling firewall...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="306"/> + <source>Disabling firewall...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="71"/> + <source>Dest Port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="72"/> + <source>Source Port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="73"/> + <source>Dest IP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="74"/> + <source>Source IP</source> + <translation type="unfinished">IP origen</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="75"/> + <source>Input interface</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="76"/> + <source>Output interface</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="77"/> + <source>Set conntrack mark</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="78"/> + <source>Match conntrack mark</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="79"/> + <source>Match conntrack state(s)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="80"/> + <source>Set mark on packet</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="81"/> + <source>Match packet information</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="87"/> + <source>Bandwidth quotas</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="89"/> + <source>Rate limit connections</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="373"/> + <source>Your protobuf version is incompatible, you need to install protobuf 3.8.0 or superior +(pip3 install --ignore-installed protobuf==3.8.0)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="397"/> + <source>Rule deleted</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="401"/> + <source>Rule added</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="420"/> + <source>You can use ',' or '-' to specify multiple ports/IPs or ranges/values:<br><br>ports: 22 or 22,443 or 50000-60000<br>IPs: 192.168.1.1 or 192.168.1.30-192.168.1.130<br>Values: echo-reply,echo-request<br>Values: new,established,related</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="440"/> + <source>Deleting rule, wait</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="443"/> + <source>Error updating rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="483"/> + <source>Adding rule, wait</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="492"/> + <source><select a statement></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="787"/> + <source>Equal</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="788"/> + <source>Not equal</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="789"/> + <source>Greater or equal than</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="790"/> + <source>Greater than</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="791"/> + <source>Less or equal than</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="792"/> + <source>Less than</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1350"/> + <source>Firewall rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="885"/> + <source>Simple</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="890"/> + <source>Advanced</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1046"/> + <source>This rule is not supported yet.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1111"/> + <source>Exclude service</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1123"/> + <source>Allow inbound connections to the selected port.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1125"/> + <source>Allow outbound connections to the selected port.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1201"/> + <source>select a statement.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1217"/> + <source>value cannot be 0 or empty.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1229"/> + <source>the value format is 1024/kbytes (or bytes, mbytes, gbytes)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1240"/> + <source>the value format is 1024/kbytes/second (or bytes, mbytes, gbytes)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1243"/> + <source>rate-limit not valid, use: bytes, kbytes, mbytes or gbytes.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1245"/> + <source>time-limit not valid, use: second, minute, hour or day</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1293"/> + <source>port not valid.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="108"/> + <source> +Supported formats: + + - Simple: 23 + - Ranges: 80-1024 + - Multiple ports: 80,443,8080 +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="134"/> + <source> +Supported formats: + + - Simple: 1.2.3.4 + - IP ranges: 1.2.3.100-1.2.3.200 + - Network ranges: 1.2.3.4/24 +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="147"/> + <source>Match input interface. Regular expressions not allowed.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="154"/> + <source>Match output interface. Regular expressions not allowed.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="161"/> + <source>Set a conntrack mark on the connection, in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="171"/> + <source>Match a conntrack mark of the connection, in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="178"/> + <source>Match conntrack states. + +Supported formats: + - Simple: new + - Multiple states separated by commas: related,new +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="193"/> + <source> +Match packet's metainformation. + +Value must be in decimal format, except for the "l4proto" option. +For l4proto it can be a lower case string, for example: + tcp + udp + icmp, + etc + +If the value is decimal for protocol or lproto, it'll use it as the code of +that protocol. +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="213"/> + <source>Set a mark on the packet matching the specified conditions. The value is in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="221"/> + <source> +Match ICMP codes. + +Supported formats: + - Simple: echo-request + - Multiple separated by commas: echo-request,echo-reply +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="234"/> + <source> +Match ICMPv6 codes. + +Supported formats: + - Simple: echo-request + - Multiple separated by commas: echo-request,echo-reply +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="247"/> + <source>Print a message when this rule matches a packet.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="254"/> + <source> +Apply quotas on connections. + +For example when: + - "quota over 10/mbytes" -> apply the Action defined (DROP) + - "quota until 10/mbytes" -> apply the Action defined (ACCEPT) + +The value must be in the format: VALUE/UNITS, for example: + - 10mbytes, 1/gbytes, etc +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="286"/> + <source> +Apply limits on connections. + +For example when: + - "limit over 10/mbytes/minute" -> apply the Action defined (DROP, ACCEPT, etc) + (When there're more than 10MB per minute, apply an Action) + + - "limit until 10/mbytes/hour" -> apply the Action defined (ACCEPT) + +The value must be in the format: VALUE/UNITS/TIME, for example: + - 10/mbytes/minute, 1/gbytes/hour, etc +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="607"/> + <source>num</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="621"/> + <source>to</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>menu_close</name> + <message> + <location filename="../../../opensnitch/service.py" line="131"/> + <source>Close</source> + <translation type="obsolete">Cerrar</translation> + </message> +</context> +<context> + <name>menu_help</name> + <message> + <location filename="../../../opensnitch/service.py" line="126"/> + <source>Help</source> + <translation type="obsolete">Ayuda</translation> + </message> +</context> +<context> + <name>menu_statistics</name> + <message> + <location filename="../../../opensnitch/service.py" line="120"/> + <source>Statistics</source> + <translation type="obsolete">Eventos</translation> + </message> +</context> +<context> + <name>messages</name> + <message> + <location filename="../../../opensnitch/service.py" line="281"/> + <source>Info</source> + <translation>Información</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="285"/> + <source>Error</source> + <translation>Fallo</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="289"/> + <source>Warning</source> + <translation>Advertencia</translation> + </message> +</context> +<context> + <name>notifications</name> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="654"/> + <source>System notifications are not available, you need to install python3-notify2.</source> + <translation>Las notificaciones de sistema no están disponibles, tienes que instalar python3-notify2.</translation> + </message> +</context> +<context> + <name>popups</name> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="115"/> + <source>Allow</source> + <translation>Permitir</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="114"/> + <source>Deny</source> + <translation>Denegar</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/> + <source>forever</source> + <translation>para siempre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="331"/> + <source>Outgoing connection</source> + <translation>Conexión saliente</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="336"/> + <source>Process launched from:</source> + <translation>Proceso ejecutado desde:</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="377"/> + <source>from this command line</source> + <translation>este comando</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="373"/> + <source>from this executable</source> + <translation>este ejecutable</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="208"/> + <source>Unknown process</source> + <translation type="obsolete">Proceso no encontrado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="50"/> + <source>until reboot</source> + <translation>Hasta reiniciar</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="379"/> + <source>to port {0}</source> + <translation>puerto {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="222"/> + <source><b>%s</b> is connecting to <b>%s</b> on %s port %d</source> + <translation type="obsolete"><b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="228"/> + <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source> + <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="442"/> + <source>to {0}</source> + <translation>a {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="382"/> + <source>from user {0}</source> + <translation>UID {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="399"/> + <source>to {0}.*</source> + <translation>a {0}.*</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="452"/> + <source>to *.{0}</source> + <translation>a *.{0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="378"/> + <source>to *{0}</source> + <translation type="obsolete">a *{0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="486"/> + <source><b>Remote</b> process %s running on <b>%s</b></source> + <translation>El proceso <b>Remoto</b> %s ejecutado en <b>%s</b></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="490"/> + <source>is connecting to <b>%s</b> on %s port %d</source> + <translation>está conectándose a <b>%s</b> en el puerto %s %d</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="502"/> + <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source> + <translation>está tratando de resolver <b>%s</b> via %s, %s puerto %d</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="386"/> + <source>from this PID</source> + <translation>de este PID</translation> + </message> + <message> + <location filename="../../../opensnitch/notifications.py" line="122"/> + <source>New outgoing connection</source> + <translation>Nueva conexión saliente</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="116"/> + <source>Reject</source> + <translation>Rechazar</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="497"/> + <source>is connecting to <b>%s</b>, %s</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/notifications.py" line="42"/> + <source>Open</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>popups2</name> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="254"/> + <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source> + <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> + </message> +</context> +<context> + <name>preferences</name> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="171"/> + <source>Exception saving config: %s</source> + <translation type="obsolete">Error al guarda la configuración: %s</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="177"/> + <source>Applying configuration on %s ...</source> + <translation type="obsolete">Aplicando configuración en %s ...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="292"/> + <source>Server address can not be empty</source> + <translation>La dirección del servidor no puede estar vacía</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="227"/> + <source>Error loading %s configuration</source> + <translation type="obsolete">Error al cargar la configuración %s</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="568"/> + <source>Configuration applied.</source> + <translation>Configuración aplicada.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="257"/> + <source>Error applying configuration: %s</source> + <translation type="obsolete">Error al aplicar la configuración: %s</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="386"/> + <source>Exception saving config: {0}</source> + <translation>Error al guardar la configuración: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="500"/> + <source>Applying configuration on {0} ...</source> + <translation>Aplicando configuración en {0} ...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="321"/> + <source>Error loading {0} configuration</source> + <translation>Error al cargar la configuración {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="570"/> + <source>Error applying configuration: {0}</source> + <translation>Error al aplicar la configuración: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> + <source>Warning</source> + <translation>Aviso</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> + <source>You must select a file for the database<br>or choose "In memory" type.</source> + <translation>Debes seleccionar un fichero para la base de datos<br>o elegir el tipo En memoria.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="401"/> + <source>DB type changed</source> + <translation>El tipo de BBDD ha cambiado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="38"/> + <source>Restart the GUI in order effects to take effect</source> + <translation>Reinicia la GUI para que los cambios surtan efecto</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="607"/> + <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source> + <translation>Pasa el ratón sobre los textos para mostrar la ayuda<br><br>Y no olvides visitar el wiki: <a href="{0}">{0}</a></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="466"/> + <source>System</source> + <translation>Sistema</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="187"/> + <source>Themes not available. Install qt-material: pip3 install qt-material</source> + <translation>Temas no disponibles. Instalar qt-material: pip3 install qt-material</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> + <source>UI theme changed</source> + <translation>Cambio del tema de la interfaz de usuario</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> + <source>Restart the GUI in order to apply the new theme</source> + <translation>Reinicie la interfaz gráfica de usuario para aplicar el nuevo tema</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="508"/> + <source>Ok</source> + <translation>De acuerdo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="472"/> + <source>Restart the GUI in order changes to take effect</source> + <translation type="obsolete">Reinicie la interfaz gráfica de usuario para que los cambios surtan efecto</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="388"/> + <source>There're no nodes connected</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="520"/> + <source>Exception saving node config {0}: {1}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="164"/> + <source>System default</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="433"/> + <source>Language changed</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>proc_details</name> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="100"/> + <source><b>Error loading process information:</b> <br><br> + +</source> + <translation><b>Error al carga la información del proceso:</b> <br><br> + +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="119"/> + <source><b>Error stopping monitoring process:</b><br><br></source> + <translation><b>Error al parar de monitorizar el proceso:</b><br><br></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="159"/> + <source>loading...</source> + <translation>cargando...</translation> + </message> +</context> +<context> + <name>rules</name> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="228"/> + <source>There're no nodes connected.</source> + <translation>No hay nodos conectados.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="271"/> + <source>Rule applied.</source> + <translation>Regla aplicada.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="123"/> + <source>Error applying rule: %s</source> + <translation type="obsolete">Error al aplicar la regla: %s</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="641"/> + <source>protocol can not be empty, or uncheck it</source> + <translation>el protocolo no puede estar vacío, o desmarca la opción</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="655"/> + <source>Protocol regexp error</source> + <translation>Error en la expresión regular del Protocolo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="659"/> + <source>process path can not be empty</source> + <translation>La ruta del ejecutable no puede estar vacía</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="673"/> + <source>Process path regexp error</source> + <translation>Error en la expresión regular del ejecutable</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="677"/> + <source>command line can not be empty</source> + <translation>El comando no puede estar vacío, o desmarca la opción</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="691"/> + <source>Command line regexp error</source> + <translation>Error en la expresión regular del Comando</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="731"/> + <source>Dest port can not be empty</source> + <translation>El puerto destino no puede estar vacío, o desmarca la opción</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="745"/> + <source>Dst port regexp error</source> + <translation>Error en la expresión regular del Puerto Destino</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="749"/> + <source>Dest host can not be empty</source> + <translation>El Host destino no puede estar vacío, o desmarca la opción</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="763"/> + <source>Dst host regexp error</source> + <translation>Error en la expresión regular del Host de destino</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="805"/> + <source>Dest IP/Network can not be empty</source> + <translation>La IP/Red de destino no puede estar vacía</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="831"/> + <source>Dst IP regexp error</source> + <translation>Error en la expresión regular de IP/Red de destino</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="843"/> + <source>User ID can not be empty</source> + <translation>El ID de Usuario no puede estar vacío, o desmarca la opción</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="857"/> + <source>User ID regexp error</source> + <translation>Error en la expresión regular del ID de Usuario</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="273"/> + <source>Error applying rule: {0}</source> + <translation>Error al aplicar la regla: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="931"/> + <source>Lists field cannot be empty</source> + <translation>El campo Listas de dominios no puede estar vacío</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="933"/> + <source>Lists field must be a directory</source> + <translation>El campo Listas debe ser un directorio</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="976"/> + <source><b>Rule not supported</b></source> + <translation><b>Tipo de regla no soportada</b></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="539"/> + <source><b>Error loading rule</b></source> + <translation><b>Error cargando la regla</b></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="245"/> + <source>There's already a rule with this name.</source> + <translation>Ya hay una regla con este nombre.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="861"/> + <source>PID field can not be empty</source> + <translation>El campo de PID no puede estar vacío</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="875"/> + <source>PID field regexp error</source> + <translation>Error en la expresión regular del PID</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="963"/> + <source>Select at least one field.</source> + <translation>Selecciona al menos un campo.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="695"/> + <source>Network interface can not be empty</source> + <translation>La interfaz de red no puede estar vacía</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="709"/> + <source>Network interface regexp error</source> + <translation>Error de la expresión regular de la interfaz de la red</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="713"/> + <source>Source port can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="727"/> + <source>Source port regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="767"/> + <source>Source IP/Network can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="793"/> + <source>Source IP regexp error</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>stats</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="313"/> + <source>Not running</source> + <translation>Parado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="314"/> + <source>Disabled</source> + <translation>Deshabilitado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="315"/> + <source>Running</source> + <translation>Interceptando</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="412"/> + <source>OpenSnitch Network Statistics</source> + <translation type="obsolete">Eventos de OpenSnitch</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="414"/> + <source>OpenSnitch Network Statistics for</source> + <translation type="obsolete">Eventos de OpenSnitch de</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1189"/> + <source> Your are about to delete this rule. </source> + <translation> Estás a punto de borrar esta regla. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> + <source> Are you sure?</source> + <translation> ¿Estás seguro?</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="636"/> + <source>OpenSnitch Network Statistics {0}</source> + <translation>Eventos de red OpenSnitch {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="638"/> + <source>OpenSnitch Network Statistics for {0}</source> + <translation>Eventos de red OpenSnitch de {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> + <source>Name</source> + <translation type="obsolete">Nombre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Address</source> + <translation type="obsolete">Dirección</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="176"/> + <source>Status</source> + <translation type="obsolete">Estado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="177"/> + <source>Hostname</source> + <translation type="obsolete">Hostname</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="183"/> + <source>Version</source> + <translation type="obsolete">Versión</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="834"/> + <source>Rules</source> + <translation type="unfinished">Reglas</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Time</source> + <translation type="obsolete">Hora</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="875"/> + <source>Action</source> + <translation type="unfinished">Acción</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> + <source>Duration</source> + <translation type="obsolete">Duración</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> + <source>Node</source> + <translation type="obsolete">Nodo</translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="18"/> + <source>Hits</source> + <translation>Total</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>Protocol</source> + <translation type="obsolete">Protocolo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2581"/> + <source>Save as CSV</source> + <translation>Guardar como CSV</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Enabled</source> + <translation type="obsolete">Habilitado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="961"/> + <source>Delete</source> + <translation>Eliminar</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="948"/> + <source>always</source> + <translation type="obsolete">siempre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="580"/> + <source><b>Error:</b><br><br>{0}</source> + <translation type="obsolete"><b>Error:</b><br><br>{0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="954"/> + <source>Disable</source> + <translation>Deshabilitar</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="956"/> + <source>Enable</source> + <translation>Habilitar</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="959"/> + <source>Duplicate</source> + <translation>Duplicar</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="960"/> + <source>Edit</source> + <translation>Editar</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1248"/> + <source>Rule not found by that name and node</source> + <translation>Regla no encontrada por ese nombre o nodo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1301"/> + <source><b>Error:</b><br><br></source> + <comment>{0}</comment> + <translation><b>Error:</b><br><br></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1308"/> + <source>Warning:</source> + <translation>Aviso:</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="940"/> + <source>Allow</source> + <translation>Permitir</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="941"/> + <source>Deny</source> + <translation>Denegar</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="945"/> + <source>Always</source> + <translation>Siempre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="946"/> + <source>Until reboot</source> + <translation>Hasta reiniciar</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> + <source> You are about to delete this rule. </source> + <translation> Estás a punto de borrar esta regla. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> + <source>Process</source> + <translation type="obsolete">Aplicación</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> + <source>Destination</source> + <translation type="obsolete">Destino</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> + <source>Rule</source> + <translation type="obsolete">Regla</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> + <source>UserID</source> + <translation type="obsolete">UserID</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="174"/> + <source>LastConnection</source> + <translation type="obsolete">ÚltimaConexión</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> + <source>Name</source> + <comment>xxxxx</comment> + <translation type="obsolete">Nombre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> + <source>Name</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Nombre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Address</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Dirección</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> + <source>Status</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Estado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> + <source>Hostname</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Hostname</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> + <source>Version</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Versión</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> + <source>Rules</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Reglas</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Time</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Hora</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> + <source>Action</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Acción</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> + <source>Duration</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Duración</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> + <source>Node</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Nodo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Enabled</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Habilitado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> + <source>Hits</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Total</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>Protocol</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Protocolo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> + <source>Process</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Aplicación</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> + <source>Destination</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Destino</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> + <source>Rule</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Regla</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> + <source>UserID</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">UserID</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> + <source>LastConnection</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">ÚltimaConexión</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="287"/> + <source>Name</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Nombre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="288"/> + <source>Address</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Dirección</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="289"/> + <source>Status</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Estado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="290"/> + <source>Hostname</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Hostname</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="423"/> + <source>Version</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Versión</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="420"/> + <source>Rules</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Reglas</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Time</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Hora</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> + <source>Action</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Acción</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> + <source>Duration</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Duración</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> + <source>Node</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Nodo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Enabled</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Habilitado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="438"/> + <source>Hits</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Total</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> + <source>Protocol</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Protocolo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Process</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Aplicación</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>Destination</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Destino</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> + <source>Rule</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Regla</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> + <source>UserID</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>UserID</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> + <source>LastConnection</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>ÚltimaConexión</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> + <source>Args</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="obsolete">Args</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> + <source>DstIP</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>IPDestino</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> + <source>DstHost</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>HostDestino</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> + <source>DstPort</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>PuertoDestino</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="175"/> + <source>Addr</source> + <translation type="obsolete">Dirección</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="181"/> + <source>Connections</source> + <translation type="obsolete">Conexiones</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="182"/> + <source>Dropped</source> + <translation type="obsolete">Rechazadas</translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="17"/> + <source>What</source> + <translation>Qué</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="931"/> + <source>Apply to</source> + <translation>Aplicar a</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="942"/> + <source>Reject</source> + <translation>Rechazar</translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="19"/> + <source>Network name</source> + <translation>Red</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="378"/> + <source>Addr</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="obsolete">Dirección</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="291"/> + <source>Uptime</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Tiempo de ejecución</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="421"/> + <source>Connections</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Conexiones</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="422"/> + <source>Dropped</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Rechazadas</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="437"/> + <source>What</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Qué</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> + <source>Precedence</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Prioritaria</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="776"/> + <source>New node connected</source> + <translation>Nuevo nodo conectado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> + <source>Description</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Descripción</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> + <source>Cmdline</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Línea en cmd</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="406"/> + <source>Export rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="407"/> + <source>Import rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="408"/> + <source>Export events to CSV</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> + <source>Quit</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="932"/> + <source>Export</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="964"/> + <source>To clipboard</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="965"/> + <source>To disk</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2523"/> + <source>Select a directory to export rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1191"/> + <source> Your are about to delete this entry. </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1678"/> + <source> You are about to delete this node. </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1687"/> + <source><b>Error deleting node</b><br><br></source> + <comment>{0}</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2478"/> + <source>Error exporting rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2552"/> + <source>Select a directory with rules to import (JSON files)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2566"/> + <source>Rules imported fine</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="211"/> + <source>WARNING</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="833"/> + <source>Details</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="835"/> + <source>New</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>stats_deleterule</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="774"/> + <source> Your are about to delete this rule. </source> + <translation type="obsolete"> Estás a punto de borrar esta regla. </translation> + </message> +</context> +<context> + <name>stats_deleterule2</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="776"/> + <source> Are you sure?</source> + <translation type="obsolete"> ¿Estás seguro?</translation> + </message> +</context> +<context> + <name>stats_disabled</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="74"/> + <source>Disabled</source> + <translation type="obsolete">Deshabilitado</translation> + </message> +</context> +<context> + <name>stats_notrunning</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="73"/> + <source>Not running</source> + <translation type="obsolete">Parado</translation> + </message> +</context> +<context> + <name>stats_running</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="75"/> + <source>Running</source> + <translation type="obsolete">Interceptando</translation> + </message> +</context> +<context> + <name>stats_wintitle</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> + <source>OpenSnitch Network Statistics</source> + <translation type="obsolete">Eventos de red OpenSnitch</translation> + </message> +</context> +<context> + <name>stats_wintitle2</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="411"/> + <source>OpenSnitch Network Statistics for</source> + <translation type="obsolete">Eventos de OpenSnitch de</translation> + </message> +</context> +</TS> diff --git a/ui/i18n/locales/eu_ES/opensnitch-eu_ES.ts b/ui/i18n/locales/eu_ES/opensnitch-eu_ES.ts new file mode 100644 index 0000000..36a572e --- /dev/null +++ b/ui/i18n/locales/eu_ES/opensnitch-eu_ES.ts @@ -0,0 +1,2695 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="eu_ES"> +<context> + <name>Dialog</name> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="34"/> + <source>opensnitch-qt</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="300"/> + <source>User ID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="334"/> + <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="647"/> + <source>TextLabel</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="437"/> + <source>Source IP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="458"/> + <source>Process ID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="601"/> + <source>Destination IP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="622"/> + <source>Dst Port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="702"/> + <source>from this executable</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="707"/> + <source>from this command line</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="712"/> + <source>this destination port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="717"/> + <source>this user</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="722"/> + <source>this destination ip</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="751"/> + <source>once</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="786"/> + <source>forever</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="337"/> + <source>Allow</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="865"/> + <source>+</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="781"/> + <source>until reboot</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="727"/> + <source>from this PID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="809"/> + <source>action</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="756"/> + <source>30s</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="761"/> + <source>5m</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="766"/> + <source>15m</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="771"/> + <source>30m</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="776"/> + <source>1h</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="14"/> + <source>Firewall</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="55"/> + <source><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="320"/> + <source>Inbound</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="346"/> + <source>Deny</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="313"/> + <source>Outbound</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="275"/> + <source>Profile</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="375"/> + <source>Allow inbound connections to a port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="378"/> + <source>Allow service (IN)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="397"/> + <source>Exclude outbound connections to a port from being intercepted</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="406"/> + <source>Allow service (OUT)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="426"/> + <source>New rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="431"/> + <source>Close</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="14"/> + <source>Firewall rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="26"/> + <source>Node</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="38"/> + <source>Enable</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="50"/> + <source>Description</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="90"/> + <source>Simple</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="154"/> + <source>Add new condition</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="177"/> + <source>Remove selected condition</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="233"/> + <source>Direction</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="248"/> + <source>IN</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="257"/> + <source>OUT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="223"/> + <source>Action</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="285"/> + <source>ACCEPT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="294"/> + <source>DROP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="303"/> + <source>REJECT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="312"/> + <source>RETURN</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="442"/> + <source>Clear</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="453"/> + <source>Delete</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="464"/> + <source>Save</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="475"/> + <source>Add</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="266"/> + <source>FORWARD</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="271"/> + <source>PREROUTING</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="276"/> + <source>POSTROUTING</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="321"/> + <source>QUEUE</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="330"/> + <source>DNAT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="335"/> + <source>SNAT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="340"/> + <source>REDIRECT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="359"/> + <source>depending on the Action (i.e.: target), the syntaxis of the parameters will vary. +Some examples: + +QUEUE -> num 0 (or 1, 2, ...) +REDIRECT, TPROXY, DNAT, SNAT, MASQUERADE: + to :22 + to 192.168.1.254:8080 + to 192.168.1.254 + to 1024-2048 (masquerade)</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>PreferencesDialog</name> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="14"/> + <source>Preferences</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="484"/> + <source>UI</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="466"/> + <source>Default timeout</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="340"/> + <source>Pop-up default duration</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="866"/> + <source>Default duration</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="323"/> + <source>Default target</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="179"/> + <source>center</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="184"/> + <source>top right</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="189"/> + <source>bottom right</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="194"/> + <source>top left</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="199"/> + <source>bottom left</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="283"/> + <source>by executable</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="288"/> + <source>by command line</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="293"/> + <source>by destination port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="298"/> + <source>by destination ip</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="303"/> + <source>by user id</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="970"/> + <source>once</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="249"/> + <source>forever</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1012"/> + <source>deny</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1021"/> + <source>allow</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="823"/> + <source>Nodes</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="829"/> + <source>Process monitor method</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="863"/> + <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="988"/> + <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="991"/> + <source>Address</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1131"/> + <source>Default log level</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1039"/> + <source>Version</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="902"/> + <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="846"/> + <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="849"/> + <source>Log file</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="921"/> + <source>HostName</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1090"/> + <source>unix:///tmp/osui.sock</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="975"/> + <source>until restart</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="980"/> + <source>always</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1102"/> + <source>/var/log/opensnitchd.log</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1107"/> + <source>/dev/stdout</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="879"/> + <source>Apply configuration to all nodes</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1146"/> + <source>Database</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1181"/> + <source>In memory</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1186"/> + <source>File</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1482"/> + <source>Close</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1493"/> + <source>Apply</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1504"/> + <source>Save</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="244"/> + <source>until reboot</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="359"/> + <source>Show advanced view by default</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="653"/> + <source>Action</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="375"/> + <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="343"/> + <source>Duration</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="263"/> + <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="266"/> + <source>Filter connections also by:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="81"/> + <source>User ID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="97"/> + <source>Destination port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="113"/> + <source>Destination IP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="463"/> + <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1200"/> + <source>Database type</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1207"/> + <source>Select</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="356"/> + <source>The advanced view allows you to easily select multiple fields to filter connections</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="110"/> + <source>If checked, this field will be selected when a pop-up is displayed</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="159"/> + <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="905"/> + <source>Default action when the GUI is disconnected</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1001"/> + <source>Debug invalid connections</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="39"/> + <source>Pop-ups</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="64"/> + <source>Default options</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="330"/> + <source>Default position on screen</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="769"/> + <source>any temporary rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="589"/> + <source>Time</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="669"/> + <source>Destination</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="637"/> + <source>Protocol</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="685"/> + <source>Process</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="605"/> + <source>Rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="621"/> + <source>Node</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="577"/> + <source>Events tab columns</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="308"/> + <source>by PID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="473"/> + <source>Disable pop-ups, only display a notification</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="496"/> + <source>Desktop notifications</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="514"/> + <source>Use system notifications</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="530"/> + <source>Use Qt notifications</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="559"/> + <source>Test</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="998"/> + <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1294"/> + <source>minutes</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1326"/> + <source>Minutes between events purges</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1352"/> + <source>days</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1365"/> + <source>Maximum days of events to keep</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="147"/> + <source>reject</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="716"/> + <source>System</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="695"/> + <source>Command line</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="708"/> + <source>Theme</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="219"/> + <source>30s</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="224"/> + <source>5m</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="229"/> + <source>15m</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="234"/> + <source>30m</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="239"/> + <source>1h</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="748"/> + <source>Rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="756"/> + <source>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI. + +Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="761"/> + <source>Don't save/Delete rules of duration</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="779"/> + <source>30s or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="784"/> + <source>5m or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="789"/> + <source>15m or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="794"/> + <source>30m or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="799"/> + <source>1h or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="724"/> + <source>Language</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>ProcessDetailsDialog</name> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="14"/> + <source>Process details</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="61"/> + <source>loading...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="81"/> + <source>CWD: loading...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="93"/> + <source>mem stats: loading...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="121"/> + <source>Status</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="135"/> + <source>Open files</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="149"/> + <source>I/O Statistics</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="163"/> + <source>Memory mapped files</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="177"/> + <source>Stack</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="191"/> + <source>Environment variables</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="210"/> + <source>Application pids</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="240"/> + <source>Start or stop monitoring this process</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="256"/> + <source>Close</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>RulesDialog</name> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="20"/> + <source>Rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> + <source>Node</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> + <source>Apply rule to all nodes</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="379"/> + <source>From this command line</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="472"/> + <source>From this executable</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="56"/> + <source>Action</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="610"/> + <source>To this IP / Network</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="97"/> + <source>once</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="132"/> + <source>always</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="902"/> + <source>To this port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="372"/> + <source>From this user ID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/> + <source>Commas or spaces are not allowed to specify multiple domains. + +Use regular expressions instead: +.*(opensnitch|duckduckgo).com +.*\.google.com + +or a single domain: +www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... +gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="603"/> + <source>www.domain.org, .*\.domain.org</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="526"/> + <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="532"/> + <source>TCP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="760"/> + <source>You can specify a single IP: +- 192.168.1.1 + +or a regular expression: +- 192\.168\.1\.[0-9]+ + +multiple IPs: +- ^(192\.168\.1\.1|172\.16\.0\.1)$ + +You can also specify a subnet: +- 192.168.1.0/24 + +Note: Commas or spaces are not allowed to separate IPs or networks.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="89"/> + <source>Duration</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="633"/> + <source>Protocol</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="750"/> + <source>To this host</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="151"/> + <source>Deny</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="191"/> + <source>Allow</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="207"/> + <source>Enable</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="238"/> + <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them. + +000-allow-localhost +001-deny-broadcast +...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="214"/> + <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one. + +You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example: + +[x] Priority - 000-priority-rule +[ ] Priority - 001-less-priority-rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="222"/> + <source>Priority rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1092"/> + <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1095"/> + <source>Case-sensitive</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="127"/> + <source>until reboot</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="980"/> + <source>To this list of domains</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="346"/> + <source>Applications</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="389"/> + <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="399"/> + <source>From this PID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="491"/> + <source>Network</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="932"/> + <source>List of domains/IPs</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="938"/> + <source>To this list of network ranges</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="945"/> + <source>To this list of IPs</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="971"/> + <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1006"/> + <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1034"/> + <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1049"/> + <source>To this list of domains +(regular expressions)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1076"/> + <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="168"/> + <source>Reject</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1151"/> + <source>Description...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="355"/> + <source><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="365"/> + <source>Is regular expression</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> + <source>is regular expression</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="863"/> + <source>Network interface</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1086"/> + <source>More</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1102"/> + <source>Don't log connections that match this rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1105"/> + <source>Don't log connections</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="148"/> + <source>Deny will just discard the connection</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="165"/> + <source>Reject will drop the connection, and kill the socket that initiated it</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="185"/> + <source>Allow will allow the connection</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="566"/> + <source>ICMP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="571"/> + <source>ICMP6</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="576"/> + <source>SCTP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="581"/> + <source>SCTP6</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="245"/> + <source>Name</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="743"/> + <source>From this IP / Network</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="872"/> + <source>From this port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="918"/> + <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>StatsDialog</name> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="34"/> + <source>OpenSnitch Network Statistics</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="333"/> + <source>Create a new rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="376"/> + <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="413"/> + <source>Status</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1793"/> + <source>-</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="451"/> + <source>Start or Stop interception</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="496"/> + <source>Events</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="94"/> + <source>Filter</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="107"/> + <source>Allow</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="116"/> + <source>Deny</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="143"/> + <source>Ex.: firefox</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="205"/> + <source>50</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="210"/> + <source>100</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="215"/> + <source>200</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="220"/> + <source>300</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="826"/> + <source>Nodes</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1700"/> + <source>Rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="995"/> + <source>enable</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="782"/> + <source>Application rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="936"/> + <source>Permanent</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="945"/> + <source>Temporary</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1063"/> + <source>Hosts</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1153"/> + <source>Applications</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1266"/> + <source>Addresses</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1356"/> + <source>Ports</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1440"/> + <source>Users</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1544"/> + <source>Connections</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1596"/> + <source>Dropped</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1648"/> + <source>Uptime</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1767"/> + <source>Version</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="233"/> + <source>Delete all intercepted events</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1022"/> + <source>Edit rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1039"/> + <source>Delete rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="927"/> + <source>All applications</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="125"/> + <source>Reject</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="180"/> + <source>0</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="777"/> + <source>2</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="954"/> + <source>System rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="637"/> + <source>Delete this node</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="653"/> + <source>Show the preferences of this node</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="669"/> + <source>Start or stop interception of this node</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>contextual_menu</name> + <message> + <location filename="../../../opensnitch/service.py" line="47"/> + <source>Statistics</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="50"/> + <source>Help</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="51"/> + <source>Close</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="48"/> + <source>Enable</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="49"/> + <source>Disable</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>firewall</name> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="91"/> + <source>Configuration applied.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="404"/> + <source>Error: {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="193"/> + <source>Applying changes...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="230"/> + <source>Error getting INPUT chain policy</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="237"/> + <source>Error getting OUTPUT chain policy</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="290"/> + <source>In order to configure firewall rules from the GUI, we need to use 'nftables' instead of 'iptables'</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="304"/> + <source>Enabling firewall...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="306"/> + <source>Disabling firewall...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="71"/> + <source>Dest Port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="72"/> + <source>Source Port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="73"/> + <source>Dest IP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="74"/> + <source>Source IP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="75"/> + <source>Input interface</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="76"/> + <source>Output interface</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="77"/> + <source>Set conntrack mark</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="78"/> + <source>Match conntrack mark</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="79"/> + <source>Match conntrack state(s)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="80"/> + <source>Set mark on packet</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="81"/> + <source>Match packet information</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="87"/> + <source>Bandwidth quotas</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="89"/> + <source>Rate limit connections</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="373"/> + <source>Your protobuf version is incompatible, you need to install protobuf 3.8.0 or superior +(pip3 install --ignore-installed protobuf==3.8.0)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="397"/> + <source>Rule deleted</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="401"/> + <source>Rule added</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="420"/> + <source>You can use ',' or '-' to specify multiple ports/IPs or ranges/values:<br><br>ports: 22 or 22,443 or 50000-60000<br>IPs: 192.168.1.1 or 192.168.1.30-192.168.1.130<br>Values: echo-reply,echo-request<br>Values: new,established,related</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="440"/> + <source>Deleting rule, wait</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="443"/> + <source>Error updating rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="483"/> + <source>Adding rule, wait</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="492"/> + <source><select a statement></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="787"/> + <source>Equal</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="788"/> + <source>Not equal</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="789"/> + <source>Greater or equal than</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="790"/> + <source>Greater than</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="791"/> + <source>Less or equal than</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="792"/> + <source>Less than</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1350"/> + <source>Firewall rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="885"/> + <source>Simple</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="890"/> + <source>Advanced</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1046"/> + <source>This rule is not supported yet.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1111"/> + <source>Exclude service</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1123"/> + <source>Allow inbound connections to the selected port.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1125"/> + <source>Allow outbound connections to the selected port.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1201"/> + <source>select a statement.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1217"/> + <source>value cannot be 0 or empty.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1229"/> + <source>the value format is 1024/kbytes (or bytes, mbytes, gbytes)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1240"/> + <source>the value format is 1024/kbytes/second (or bytes, mbytes, gbytes)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1243"/> + <source>rate-limit not valid, use: bytes, kbytes, mbytes or gbytes.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1245"/> + <source>time-limit not valid, use: second, minute, hour or day</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1293"/> + <source>port not valid.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="108"/> + <source> +Supported formats: + + - Simple: 23 + - Ranges: 80-1024 + - Multiple ports: 80,443,8080 +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="134"/> + <source> +Supported formats: + + - Simple: 1.2.3.4 + - IP ranges: 1.2.3.100-1.2.3.200 + - Network ranges: 1.2.3.4/24 +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="147"/> + <source>Match input interface. Regular expressions not allowed.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="154"/> + <source>Match output interface. Regular expressions not allowed.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="161"/> + <source>Set a conntrack mark on the connection, in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="171"/> + <source>Match a conntrack mark of the connection, in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="178"/> + <source>Match conntrack states. + +Supported formats: + - Simple: new + - Multiple states separated by commas: related,new +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="193"/> + <source> +Match packet's metainformation. + +Value must be in decimal format, except for the "l4proto" option. +For l4proto it can be a lower case string, for example: + tcp + udp + icmp, + etc + +If the value is decimal for protocol or lproto, it'll use it as the code of +that protocol. +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="213"/> + <source>Set a mark on the packet matching the specified conditions. The value is in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="221"/> + <source> +Match ICMP codes. + +Supported formats: + - Simple: echo-request + - Multiple separated by commas: echo-request,echo-reply +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="234"/> + <source> +Match ICMPv6 codes. + +Supported formats: + - Simple: echo-request + - Multiple separated by commas: echo-request,echo-reply +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="247"/> + <source>Print a message when this rule matches a packet.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="254"/> + <source> +Apply quotas on connections. + +For example when: + - "quota over 10/mbytes" -> apply the Action defined (DROP) + - "quota until 10/mbytes" -> apply the Action defined (ACCEPT) + +The value must be in the format: VALUE/UNITS, for example: + - 10mbytes, 1/gbytes, etc +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="286"/> + <source> +Apply limits on connections. + +For example when: + - "limit over 10/mbytes/minute" -> apply the Action defined (DROP, ACCEPT, etc) + (When there're more than 10MB per minute, apply an Action) + + - "limit until 10/mbytes/hour" -> apply the Action defined (ACCEPT) + +The value must be in the format: VALUE/UNITS/TIME, for example: + - 10/mbytes/minute, 1/gbytes/hour, etc +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="607"/> + <source>num</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="621"/> + <source>to</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>messages</name> + <message> + <location filename="../../../opensnitch/service.py" line="281"/> + <source>Info</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="285"/> + <source>Error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="289"/> + <source>Warning</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>notifications</name> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="654"/> + <source>System notifications are not available, you need to install python3-notify2.</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>popups</name> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="50"/> + <source>until reboot</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/> + <source>forever</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="115"/> + <source>Allow</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="114"/> + <source>Deny</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="331"/> + <source>Outgoing connection</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="336"/> + <source>Process launched from:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="373"/> + <source>from this executable</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="377"/> + <source>from this command line</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="379"/> + <source>to port {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="442"/> + <source>to {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="382"/> + <source>from user {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="399"/> + <source>to {0}.*</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="452"/> + <source>to *.{0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="486"/> + <source><b>Remote</b> process %s running on <b>%s</b></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="490"/> + <source>is connecting to <b>%s</b> on %s port %d</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="502"/> + <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="386"/> + <source>from this PID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/notifications.py" line="122"/> + <source>New outgoing connection</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="116"/> + <source>Reject</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="497"/> + <source>is connecting to <b>%s</b>, %s</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/notifications.py" line="42"/> + <source>Open</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>preferences</name> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="292"/> + <source>Server address can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="568"/> + <source>Configuration applied.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="386"/> + <source>Exception saving config: {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="500"/> + <source>Applying configuration on {0} ...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="321"/> + <source>Error loading {0} configuration</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="570"/> + <source>Error applying configuration: {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> + <source>Warning</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> + <source>You must select a file for the database<br>or choose "In memory" type.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="401"/> + <source>DB type changed</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="38"/> + <source>Restart the GUI in order effects to take effect</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="607"/> + <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="466"/> + <source>System</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="187"/> + <source>Themes not available. Install qt-material: pip3 install qt-material</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> + <source>UI theme changed</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> + <source>Restart the GUI in order to apply the new theme</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="508"/> + <source>Ok</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="388"/> + <source>There're no nodes connected</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="520"/> + <source>Exception saving node config {0}: {1}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="164"/> + <source>System default</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="433"/> + <source>Language changed</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>proc_details</name> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="100"/> + <source><b>Error loading process information:</b> <br><br> + +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="119"/> + <source><b>Error stopping monitoring process:</b><br><br></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="159"/> + <source>loading...</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>rules</name> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="228"/> + <source>There're no nodes connected.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="271"/> + <source>Rule applied.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="641"/> + <source>protocol can not be empty, or uncheck it</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="655"/> + <source>Protocol regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="659"/> + <source>process path can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="673"/> + <source>Process path regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="677"/> + <source>command line can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="691"/> + <source>Command line regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="731"/> + <source>Dest port can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="745"/> + <source>Dst port regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="749"/> + <source>Dest host can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="763"/> + <source>Dst host regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="805"/> + <source>Dest IP/Network can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="831"/> + <source>Dst IP regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="843"/> + <source>User ID can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="857"/> + <source>User ID regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="273"/> + <source>Error applying rule: {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="539"/> + <source><b>Error loading rule</b></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="931"/> + <source>Lists field cannot be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="933"/> + <source>Lists field must be a directory</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="976"/> + <source><b>Rule not supported</b></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="245"/> + <source>There's already a rule with this name.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="861"/> + <source>PID field can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="875"/> + <source>PID field regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="963"/> + <source>Select at least one field.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="695"/> + <source>Network interface can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="709"/> + <source>Network interface regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="713"/> + <source>Source port can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="727"/> + <source>Source port regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="767"/> + <source>Source IP/Network can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="793"/> + <source>Source IP regexp error</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>stats</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="313"/> + <source>Not running</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="314"/> + <source>Disabled</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="315"/> + <source>Running</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1189"/> + <source> Your are about to delete this rule. </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> + <source> Are you sure?</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="636"/> + <source>OpenSnitch Network Statistics {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="638"/> + <source>OpenSnitch Network Statistics for {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2581"/> + <source>Save as CSV</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="961"/> + <source>Delete</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1301"/> + <source><b>Error:</b><br><br></source> + <comment>{0}</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1308"/> + <source>Warning:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="940"/> + <source>Allow</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="941"/> + <source>Deny</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="945"/> + <source>Always</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="946"/> + <source>Until reboot</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="954"/> + <source>Disable</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="956"/> + <source>Enable</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="959"/> + <source>Duplicate</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="960"/> + <source>Edit</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1248"/> + <source>Rule not found by that name and node</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> + <source> You are about to delete this rule. </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="287"/> + <source>Name</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="288"/> + <source>Address</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="289"/> + <source>Status</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="290"/> + <source>Hostname</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="423"/> + <source>Version</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="420"/> + <source>Rules</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Time</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> + <source>Action</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> + <source>Duration</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> + <source>Node</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Enabled</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="438"/> + <source>Hits</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> + <source>Protocol</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Process</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>Destination</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> + <source>Rule</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> + <source>UserID</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> + <source>LastConnection</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> + <source>DstIP</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> + <source>DstHost</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> + <source>DstPort</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="17"/> + <source>What</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="18"/> + <source>Hits</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="931"/> + <source>Apply to</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="942"/> + <source>Reject</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="19"/> + <source>Network name</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="291"/> + <source>Uptime</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="421"/> + <source>Connections</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="422"/> + <source>Dropped</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="437"/> + <source>What</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> + <source>Precedence</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="776"/> + <source>New node connected</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> + <source>Description</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> + <source>Cmdline</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="406"/> + <source>Export rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="407"/> + <source>Import rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="408"/> + <source>Export events to CSV</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> + <source>Quit</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="932"/> + <source>Export</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="964"/> + <source>To clipboard</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="965"/> + <source>To disk</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2523"/> + <source>Select a directory to export rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1191"/> + <source> Your are about to delete this entry. </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1678"/> + <source> You are about to delete this node. </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1687"/> + <source><b>Error deleting node</b><br><br></source> + <comment>{0}</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2478"/> + <source>Error exporting rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2552"/> + <source>Select a directory with rules to import (JSON files)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2566"/> + <source>Rules imported fine</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="211"/> + <source>WARNING</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="833"/> + <source>Details</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="834"/> + <source>Rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="835"/> + <source>New</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="875"/> + <source>Action</source> + <translation type="unfinished"></translation> + </message> +</context> +</TS> diff --git a/ui/i18n/locales/fi_FI/opensnitch-fi_FI.ts b/ui/i18n/locales/fi_FI/opensnitch-fi_FI.ts new file mode 100644 index 0000000..8361735 --- /dev/null +++ b/ui/i18n/locales/fi_FI/opensnitch-fi_FI.ts @@ -0,0 +1,2874 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="fi_FI" sourcelanguage="en"> +<context> + <name>Dialog</name> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="34"/> + <source>opensnitch-qt</source> + <translation></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="300"/> + <source>User ID</source> + <translation>Käyttäjä-ID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="334"/> + <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source> + <translation><html><head/><body><p><span style=" font-weight:600;">Käynnistetty kohteesta</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="647"/> + <source>TextLabel</source> + <translation></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="437"/> + <source>Source IP</source> + <translation>Lähde-IP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="458"/> + <source>Process ID</source> + <translation>Prosessi-ID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="601"/> + <source>Destination IP</source> + <translation>Kohde-IP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="622"/> + <source>Dst Port</source> + <translation>Kohdeportti</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="702"/> + <source>from this executable</source> + <translation>tästä ohjelmatiedostosta</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="707"/> + <source>from this command line</source> + <translation>tästä komentorivistä</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="712"/> + <source>this destination port</source> + <translation>tästä kohdeportista</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="717"/> + <source>this user</source> + <translation>tältä käyttäjältä</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="722"/> + <source>this destination ip</source> + <translation>tästä kohde-IP:stä</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="727"/> + <source>from this PID</source> + <translation>tästä PID:stä</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="751"/> + <source>once</source> + <translation>kerran</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="756"/> + <source>30s</source> + <translation>30s</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="761"/> + <source>5m</source> + <translation>5m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="766"/> + <source>15m</source> + <translation>15m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="771"/> + <source>30m</source> + <translation>30m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="776"/> + <source>1h</source> + <translation>1t</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="781"/> + <source>until reboot</source> + <translation>uudelleenkäynnistykseen asti</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="786"/> + <source>forever</source> + <translation>ikuisesti</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="809"/> + <source>action</source> + <translation>toiminto</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="337"/> + <source>Allow</source> + <translation>Salli</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="865"/> + <source>+</source> + <translation>+</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="14"/> + <source>Firewall</source> + <translation>Palomuuri</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="55"/> + <source><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></source> + <translation><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Palomuuri</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="275"/> + <source>Profile</source> + <translation>Profiili</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="346"/> + <source>Deny</source> + <translation>Estä</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="313"/> + <source>Outbound</source> + <translation>Lähtevä</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="320"/> + <source>Inbound</source> + <translation>Tuleva</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="375"/> + <source>Allow inbound connections to a port</source> + <translation>Salli tulevat yhteydet porttiin</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="378"/> + <source>Allow service (IN)</source> + <translation>Salli palvelu (IN)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="397"/> + <source>Exclude outbound connections to a port from being intercepted</source> + <translation>Poissulje porttiin lähtevät yhteydet sieppaukselta</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="406"/> + <source>Allow service (OUT)</source> + <translation>Salli palvelu (OUT)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="426"/> + <source>New rule</source> + <translation>Uusi sääntö</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="421"/> + <source>Close</source> + <translation>Sulje</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="14"/> + <source>Firewall rule</source> + <translation>Palomuurisääntö</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="26"/> + <source>Node</source> + <translation>Solmu</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="38"/> + <source>Enable</source> + <translation>Ota käyttöön</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="50"/> + <source>Description</source> + <translation>Kuvaus</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="90"/> + <source>Simple</source> + <translation>Yksinkertainen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="154"/> + <source>Add new condition</source> + <translation>Lisää uusi ehto</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="177"/> + <source>Remove selected condition</source> + <translation>Poista valittu ehto</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="221"/> + <source>Direction</source> + <translation>Suunta</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="232"/> + <source>IN</source> + <translation>IN</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="241"/> + <source>OUT</source> + <translation>OUT</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="250"/> + <source>FORWARD</source> + <translation>FORWARD</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="255"/> + <source>PREROUTING</source> + <translation>PREROUTING</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="260"/> + <source>POSTROUTING</source> + <translation>POSTROUTING</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="268"/> + <source>Action</source> + <translation>Toiminto</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="279"/> + <source>ACCEPT</source> + <translation>ACCEPT</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="288"/> + <source>DROP</source> + <translation>DROP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="297"/> + <source>REJECT</source> + <translation>REJECT</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="306"/> + <source>RETURN</source> + <translation>RETURN</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="315"/> + <source>QUEUE</source> + <translation>QUEUE</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="323"/> + <source>DNAT</source> + <translation>DNAT</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="328"/> + <source>SNAT</source> + <translation>SNAT</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="333"/> + <source>REDIRECT</source> + <translation>REDIRECT</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="349"/> + <source>depending on the Action (i.e.: target), the syntaxis of the parameters will vary. +Some examples: + +QUEUE -> num 0 (or 1, 2, ...) +REDIRECT, TPROXY, DNAT, SNAT, MASQUERADE: + to :22 + to 192.168.1.254:8080 + to 192.168.1.254 + to 1024-2048 (masquerade)</source> + <translation>toiminnosta (eli kohteesta) riippuen parametrien syntaksi vaihtelee. +Joitakin esimerkkejä: + +QUEUE -> num 0 (or 1, 2, ...) +REDIRECT, TPROXY, DNAT, SNAT, MASQUERADE: + to :22 + to 192.168.1.254:8080 + to 192.168.1.254 + to 1024-2048 (masquerade)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="432"/> + <source>Clear</source> + <translation>Tyhjennä</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="443"/> + <source>Delete</source> + <translation>Poista</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="454"/> + <source>Save</source> + <translation>Tallenna</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="465"/> + <source>Add</source> + <translation>Lisää</translation> + </message> +</context> +<context> + <name>PreferencesDialog</name> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="14"/> + <source>Preferences</source> + <translation>Asetukset</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="39"/> + <source>Pop-ups</source> + <translation>Ponnahdusikkunat</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="64"/> + <source>Default options</source> + <translation>Oletusasetukset</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="110"/> + <source>If checked, this field will be selected when a pop-up is displayed</source> + <translation>Jos tämä kenttä on valittuna, se valitaan, kun ponnahdusikkuna tulee näkyviin</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="81"/> + <source>User ID</source> + <translation>Käyttäjä-ID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="97"/> + <source>Destination port</source> + <translation>Kohdeportti</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="113"/> + <source>Destination IP</source> + <translation>Kohde-IP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1012"/> + <source>deny</source> + <translation>estä</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1021"/> + <source>allow</source> + <translation>salli</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="147"/> + <source>reject</source> + <translation>hylkää</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="159"/> + <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source> + <translation><html><head/><body><p>Ponnahdusikkunan oletustoiminto.</p><p>Kun uutta lähtevää yhteyttä ollaan muodostamassa, tämä toiminto valitaan oletusarvoisesti.</p><p><br/></p><p>Kun ponnahdusikkunassa kysytään käyttäjältä yhteyden sallimista tai kieltämistä:</p><p>1. Uudet lähtevät yhteydet kielletään.</p><p>2. Tunnetut yhteydet sallitaan tai kielletään käyttäjän määrittelemien sääntöjen perusteella.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="653"/> + <source>Action</source> + <translation>Toiminto</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="179"/> + <source>center</source> + <translation>keskellä</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="184"/> + <source>top right</source> + <translation>ylhäällä, oikealla</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="189"/> + <source>bottom right</source> + <translation>alhaalla, oikealla</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="194"/> + <source>top left</source> + <translation>ylhäällä, vasemmalla</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="199"/> + <source>bottom left</source> + <translation>alhaalla, vasemmalla</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="970"/> + <source>once</source> + <translation>kerran</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="219"/> + <source>30s</source> + <translation>30s</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="224"/> + <source>5m</source> + <translation>5m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="229"/> + <source>15m</source> + <translation>15m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="234"/> + <source>30m</source> + <translation>30m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="239"/> + <source>1h</source> + <translation>1t</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="244"/> + <source>until reboot</source> + <translation>uudelleenkäynnistykseen asti</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="249"/> + <source>forever</source> + <translation>ikuisesti</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="263"/> + <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source> + <translation><html><head/><body><p>Oletusarvoisesti kun uusi ponnahdusikkuna tulee näkyviin, yksinkertaisimmillaan voit suodattaa yhteyksiä tai sovelluksia yhden yhteyden ominaisuuden perusteella (ohjelmatiedosto, portti, IP-osoite jne.).</p><p>Vaihtoehtojen avulla voit valita useita kenttiä, joiden perusteella suodatat yhteyksiä.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="266"/> + <source>Filter connections also by:</source> + <translation>Suodata yhteydet myös seuraavilla tavoilla:</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="283"/> + <source>by executable</source> + <translation>ohjelmatiedostoston mukaan</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="288"/> + <source>by command line</source> + <translation>komentorivin mukaan</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="293"/> + <source>by destination port</source> + <translation>kohdeportin mukaan</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="298"/> + <source>by destination ip</source> + <translation>kohde-IP:n mukaan</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="303"/> + <source>by user id</source> + <translation>käyttäjä-ID:n mukaan</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="308"/> + <source>by PID</source> + <translation>PID:n mukaan</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="323"/> + <source>Default target</source> + <translation>Oletuskohde</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="330"/> + <source>Default position on screen</source> + <translation>Oletussijainti näytössä</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="340"/> + <source>Pop-up default duration</source> + <translation>Ponnahdusikkunan oletuskesto</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="343"/> + <source>Duration</source> + <translation>Kesto</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="356"/> + <source>The advanced view allows you to easily select multiple fields to filter connections</source> + <translation>Edistyneessä näkymässä voit helposti valita useita kenttiä suodatettavia yhteyksiä varten</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="359"/> + <source>Show advanced view by default</source> + <translation>Näytä laajennettu näkymä oletusarvoisesti</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="375"/> + <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source> + <translation><html><head/><body><p>Jos tämä on valittuna, ponnahdusikkunat näytetään, kun laajennettu näkymä on aktiivinen.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="463"/> + <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source> + <translation><html><head/><body><p>Tämä aikakatkaisu on lähtölaskenta, joka näkyy, kun ponnahdusikkuna näytetään.</p><p>Jos ponnahdusikkunaan ei vastata, käytetään oletusasetuksia.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="466"/> + <source>Default timeout</source> + <translation>Oletusaikakatkaisu</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="473"/> + <source>Disable pop-ups, only display a notification</source> + <translation>Poista ponnahdusikkunat käytöstä ja näytä vain ilmoitus</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="484"/> + <source>UI</source> + <translation>Käyttöliittymä</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="496"/> + <source>Desktop notifications</source> + <translation>Työpöytäilmoitukset</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="514"/> + <source>Use system notifications</source> + <translation>Käytä järjestelmän ilmoituksia</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="530"/> + <source>Use Qt notifications</source> + <translation>Käytä Qt-ilmoituksia</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="559"/> + <source>Test</source> + <translation>Testaa</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="577"/> + <source>Events tab columns</source> + <translation>Tapahtumavälilehden sarakkeet</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="589"/> + <source>Time</source> + <translation>Aika</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="605"/> + <source>Rule</source> + <translation>Sääntö</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="621"/> + <source>Node</source> + <translation>Solmu</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="637"/> + <source>Protocol</source> + <translation>Protokolla</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="669"/> + <source>Destination</source> + <translation>Kohde</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="685"/> + <source>Process</source> + <translation>Prosessi</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="695"/> + <source>Command line</source> + <translation>Komentorivi</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="708"/> + <source>Theme</source> + <translation>Teema</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="716"/> + <source>System</source> + <translation>Järjestelmä</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="724"/> + <source>Language</source> + <translation>Kieli</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="748"/> + <source>Rules</source> + <translation>Säännöt</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="756"/> + <source>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI. + +Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</source> + <translation>Kun tämä vaihtoehto on valittuna, valitun keston sääntöjä ei lisätä käyttöliittymän väliaikaisten sääntöjen luetteloon. + +Väliaikaiset säännöt ovat edelleen voimassa, ja voit käyttää niitä, kun sinua pyydetään sallimaan/kieltämään uusi yhteys.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="761"/> + <source>Don't save/Delete rules of duration</source> + <translation>Älä tallenna/poista sääntöjä kestolta</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="769"/> + <source>any temporary rules</source> + <translation>miltään väliaikaisilta säännöiltä</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="779"/> + <source>30s or less</source> + <translation>30s tai vähemmältä</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="784"/> + <source>5m or less</source> + <translation>5m tai vähemmältä</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="789"/> + <source>15m or less</source> + <translation>15m tai vähemmältä</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="794"/> + <source>30m or less</source> + <translation>30m tai vähemmältä</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="799"/> + <source>1h or less</source> + <translation>1t tai vähemmältä</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="823"/> + <source>Nodes</source> + <translation>Solmut</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="829"/> + <source>Process monitor method</source> + <translation>Prosessin monitorointimekanismi</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="846"/> + <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source> + <translation><html><head/><body><p>Lokitiedosto lokien kirjoittamista varten.<br/></p><p>/dev/stdout tulostaa lokit vakiolähdölle.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="849"/> + <source>Log file</source> + <translation>Logitiedosto</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="863"/> + <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source> + <translation><html><head/><body><p>Oletuskesto otetaan käyttöön, kun käyttöliittymää ei ole kytketty.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="866"/> + <source>Default duration</source> + <translation>Oletuskesto</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="879"/> + <source>Apply configuration to all nodes</source> + <translation>Sovella asetuksia kaikkiin solmuihin</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="902"/> + <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source> + <translation><html><head/><body><p>Vakiotoiminto suoritetaan, kun käyttöliittymää ei ole yhdistetty.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="905"/> + <source>Default action when the GUI is disconnected</source> + <translation>Oletustoiminto, kun käyttöliittymän yhteys on katkaistu</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="921"/> + <source>HostName</source> + <translation>Isäntänimi</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="975"/> + <source>until restart</source> + <translation>uudelleenkäynnistykseen asti</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="980"/> + <source>always</source> + <translation>aina</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="988"/> + <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source> + <translation><html><head/><body><p>Solmun osoite.</p><p>Esimerkintä: unix:///tmp/osui.sock (unix:// on pakollinen, jos kyseessä on Unix-soketti)</p><p>Se voi olla myös IP-osoite portin kanssa: 127.0.0.1:50051</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="991"/> + <source>Address</source> + <translation>Osoite</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="998"/> + <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source> + <translation><html><head/><body><p>Jos valittuna, OpenSnitch pyytää sinua sallimaan tai kieltämään yhteydet, joihin ei ole liitetty PID:iä, useista syistä, useimmiten huonojen yksien takia.</p><p>Ponnahdusikkuna sisältää vain tietoja verkkoyhteydestä.</p><p>Jossain tilanteissa nämä yhteydet ovat kuitenkin kelvollisia yhteyksiä, kuten esimerkiksi luodessasi VPN:ää WireGuardin avulla.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1001"/> + <source>Debug invalid connections</source> + <translation>Vianmääritä virheellisiä yhteyksiä</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1039"/> + <source>Version</source> + <translation>Versio</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1090"/> + <source>unix:///tmp/osui.sock</source> + <translation>unix:///tmp/osui.sock</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1102"/> + <source>/var/log/opensnitchd.log</source> + <translation>/var/log/opensnitchd.log</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1107"/> + <source>/dev/stdout</source> + <translation>/dev/stdout</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1131"/> + <source>Default log level</source> + <translation>Oletuslogitaso</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1146"/> + <source>Database</source> + <translation>Tietokanta</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1181"/> + <source>In memory</source> + <translation>Muistissa</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1186"/> + <source>File</source> + <translation>Tiedostossa</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1200"/> + <source>Database type</source> + <translation>Tietokantatyyppi</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1207"/> + <source>Select</source> + <translation>Valitse</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1294"/> + <source>minutes</source> + <translation>minuuttia</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1326"/> + <source>Minutes between events purges</source> + <translation>Tapahtumien puhdistusväli minuuteissa</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1352"/> + <source>days</source> + <translation>päivää</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1365"/> + <source>Maximum days of events to keep</source> + <translation>Tapahtumien enimmäissäilytys päivissä</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1482"/> + <source>Close</source> + <translation>Sulje</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1493"/> + <source>Apply</source> + <translation>Toteuta</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1504"/> + <source>Save</source> + <translation>Tallenna</translation> + </message> +</context> +<context> + <name>ProcessDetailsDialog</name> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="14"/> + <source>Process details</source> + <translation>Prosessin tiedot</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="61"/> + <source>loading...</source> + <translation>ladataan...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="81"/> + <source>CWD: loading...</source> + <translation>CWD: ladataan...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="93"/> + <source>mem stats: loading...</source> + <translation>muistitilastot: ladataan...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="121"/> + <source>Status</source> + <translation>Tila</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="135"/> + <source>Open files</source> + <translation>Avoimet tiedostot</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="149"/> + <source>I/O Statistics</source> + <translation>I/O-tilastot</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="163"/> + <source>Memory mapped files</source> + <translation>Muistikartoitetut tiedostot</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="177"/> + <source>Stack</source> + <translation>Pino</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="191"/> + <source>Environment variables</source> + <translation>Ympäristömuuttujat</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="210"/> + <source>Application pids</source> + <translation>Sovelluksen PID:it</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="240"/> + <source>Start or stop monitoring this process</source> + <translation>Aloita tai pysäytä tämän prosessin monitorointi</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="256"/> + <source>Close</source> + <translation>SUlje</translation> + </message> +</context> +<context> + <name>RulesDialog</name> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="20"/> + <source>Rule</source> + <translation>Sääntö</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="56"/> + <source>Action</source> + <translation>Toiminto</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="89"/> + <source>Duration</source> + <translation>Kesto</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="97"/> + <source>once</source> + <translation>kerran</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="127"/> + <source>until reboot</source> + <translation>uudelleenkäynnistykseen asti</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="132"/> + <source>always</source> + <translation>aina</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="148"/> + <source>Deny will just discard the connection</source> + <translation>Esto vain sivuuttaa yhteyden</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="151"/> + <source>Deny</source> + <translation>Estä</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="165"/> + <source>Reject will drop the connection, and kill the socket that initiated it</source> + <translation>Hylkäys pudottaa yhteyden ja tappaa sen aloittaneen liitännän</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="168"/> + <source>Reject</source> + <translation>Hylkää</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="185"/> + <source>Allow will allow the connection</source> + <translation>Salli sallii yhteyden</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="191"/> + <source>Allow</source> + <translation>Salli</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="207"/> + <source>Enable</source> + <translation>Ota käyttöön</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="214"/> + <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one. + +You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example: + +[x] Priority - 000-priority-rule +[ ] Priority - 001-less-priority-rule</source> + <translation>Jos valintaruutu on valittuna, tämä sääntö on etusijalla muihin sääntöihin nähden. Muita sääntöjä ei tarkisteta tämän säännön jälkeen. + +Sinun on nimettävä sääntö siten, että se tarkistetaan ensimmäisenä, koska säännöt tarkistetaan aakkosjärjestyksessä. Esimerkiksi: + +[x] Prioriteetti - 000-prioriteettisääntö +[ ] Prioriteetti - 001-alhaisempi prioriteettisääntö</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="222"/> + <source>Priority rule</source> + <translation>Prioriteettisääntö</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="238"/> + <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them. + +000-allow-localhost +001-deny-broadcast +...</source> + <translation>Säännöt tarkistetaan aakkosjärjestyksessä, joten voit nimetä ne sen mukaan ja asettaa ne tärkeysjärjestykseen. + +000-allow-localhost +001-deny-broadcast +...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="245"/> + <source>Name</source> + <translation>Nimi</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> + <source>Node</source> + <translation>Solmu</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> + <source>Apply rule to all nodes</source> + <translation>Sovella sääntöä kaikkiin solmuihin</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="346"/> + <source>Applications</source> + <translation>Sovellukset</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="355"/> + <source><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></source> + <translation><html><head/><body><p>Tämän kentän arvo on aina suoritettavan tiedoston absoluuttinen polku: /path/to/binary<br/></p><p> Esimerkkejä:</p><p>- Simple: /</p><p>- Useita polkuja: ^/usr/lib(64|)/firefox/firefox$</p><p>- Useita binäärejä: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Kielletään/sallitaan suoritukset /tmp:stä:</p><p>^/(var/|)tmp/.*$<br/></p><p> Lisää esimerkkejä löydät <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki-sivulta</a> tai kysy <a href="https://github.com/evilsocket/opensnitch/discussions">keskustelufoorumeilla</a>.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="365"/> + <source>Is regular expression</source> + <translation>Onko säännöllinen lauseke</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="372"/> + <source>From this user ID</source> + <translation>Tältä käyttäjä-ID:ltä</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="379"/> + <source>From this command line</source> + <translation>Tästä komentorivistä</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="389"/> + <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source> + <translation><html><head/><body><p>Tämä kenttä sisältää käyttäjän suorittaman komentorivin ja vastaa sitä.<br/></p><p> Jos käyttäjä kirjoitti komennon, vain komento näkyy:</p><p>telnet 1.2.3.4<br/></p><p> Jos käyttäjä kirjoitti komennon absoluuttisen tai suhteellisen polun, se näkyy:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../../usr/bin/telnet 1.2.3.4.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="399"/> + <source>From this PID</source> + <translation>Tästä PID:istä</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="466"/> + <source>From this executable</source> + <translation>Tästä ohjelmatiedostosta</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="473"/> + <source>is regular expression</source> + <translation>on säännöllinen lauseke</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="485"/> + <source>Network</source> + <translation>Verkko</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="520"/> + <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source> + <translation><html><head/><body><p>Vain TCP, UDP tai UDPLITE ovat sallittuja</p><p>Voit käyttää regexp:iä, esim: ^(TCP|UDP)$</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="526"/> + <source>TCP</source> + <translation>TCP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="560"/> + <source>ICMP</source> + <translation>ICMP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="565"/> + <source>ICMP6</source> + <translation>ICMP6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="570"/> + <source>SCTP</source> + <translation>SCTP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="575"/> + <source>SCTP6</source> + <translation>SCTP6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="586"/> + <source>Commas or spaces are not allowed to specify multiple domains. + +Use regular expressions instead: +.*(opensnitch|duckduckgo).com +.*\.google.com + +or a single domain: +www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... +gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source> + <translation>Pilkut tai välilyönnit eivät ole sallittuja useiden toimialueiden määrittämisessä. + +Käytä sen sijaan säännöllisiä lausekkeita: +.*(opensnitch|duckduckgo).com". +.*\.google.com + +tai yksittäinen verkkotunnus: +www.gnu.org - se vastaa vain www.gnu.org, eikä ftp.gnu.org, eikä www2.gnu.org, ... +gnu.org - vain gnu.org, www.gnu.org, ftp.gnu.org, ...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="597"/> + <source>www.domain.org, .*\.domain.org</source> + <translation>www.domain.org, .*\.domain.org</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="604"/> + <source>To this IP / Network</source> + <translation>Tähän IP-osoitteeseen / verkkoon</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="627"/> + <source>Protocol</source> + <translation>Protokolla</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="754"/> + <source>You can specify a single IP: +- 192.168.1.1 + +or a regular expression: +- 192\.168\.1\.[0-9]+ + +multiple IPs: +- ^(192\.168\.1\.1|172\.16\.0\.1)$ + +You can also specify a subnet: +- 192.168.1.0/24 + +Note: Commas or spaces are not allowed to separate IPs or networks.</source> + <translation>Voit määrittää yhden IP-osoitteen: +- 192.168.1.1 + +tai säännöllisen lausekkeen: +- 192\.168\.1\.[0-9]+ + +useita IP-osoitteita: +- ^(192\.168\.1\.1|172\.16\.0\.1)$ + +Voit myös määrittää aliverkon: +- 192.168.1.0/24 + +Huomautus: Pilkut tai välilyönnit eivät saa erottaa IP-osoitteita tai verkkoja toisistaan.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="659"/> + <source>LAN</source> + <translation>LAN</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="664"/> + <source>MULTICAST</source> + <translation>MULTICAST</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="669"/> + <source>127.0.0.0/8</source> + <translation>127.0.0.0/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="674"/> + <source>192.168.0.0/24</source> + <translation>192.168.0.0/24</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="679"/> + <source>192.168.1.0/24</source> + <translation>192.168.1.0/24</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="684"/> + <source>192.168.2.0/24</source> + <translation>192.168.2.0/24</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="689"/> + <source>192.168.0.0/16</source> + <translation>192.168.0.0/16</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="694"/> + <source>169.254.0.0/16</source> + <translation>169.254.0.0/16</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="699"/> + <source>172.16.0.0/12</source> + <translation>172.16.0.0/12</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="704"/> + <source>10.0.0.0/8</source> + <translation>10.0.0.0/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="709"/> + <source>::1/128</source> + <translation>::1/128</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="714"/> + <source>fc00::/7</source> + <translation>fc00::/7</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="719"/> + <source>ff00::/8</source> + <translation>ff00::/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="724"/> + <source>fe80::/10</source> + <translation>fe80::/10</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="729"/> + <source>fd00::/8</source> + <translation>fd00::/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="737"/> + <source>From this IP / Network</source> + <translation>Tästä IP-osoitteesta / verkosta</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="744"/> + <source>To this host</source> + <translation>Tälle isännälle</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="857"/> + <source>Network interface</source> + <translation>Verkkoliitäntä</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="866"/> + <source>From this port</source> + <translation>Tästä portista</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="912"/> + <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> + <translation><html><head/><body><p>Voit määrittää useita portteja käyttämällä säännöllisiä lausekkeita:</p><p>- 53, 80 tai 443:</p><p>^(53|80|443)$</p><p><br/></p> <p> - 53, 443 tai 5551, 5552, 5553, jne:</p><p>^(53|443|555[0-9])$.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="896"/> + <source>To this port</source> + <translation>Tähän porttiin</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="926"/> + <source>List of domains/IPs</source> + <translation>Luettelo verkkotunnuksista/IP-osoitteista</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="932"/> + <source>To this list of network ranges</source> + <translation>Tähän verkkoalueiden luetteloon</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="939"/> + <source>To this list of IPs</source> + <translation>Tähän IP-osoitteiden luetteloon</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="965"/> + <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation><html><head/><body><p>Valitse hakemisto, jossa on estettävien tai sallittujen IP-osoitteiden luettelon sisältäviä tiedostoja:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>jne.</p><p>Yksi IP-osoite per rivi. Tyhjät tai #-alkuiset rivit jätetään huomiotta.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="974"/> + <source>To this list of domains</source> + <translation>Tähän verkkotunnusten luetteloon</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1000"/> + <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation><html><head/><body><p>Valitse hakemisto, jossa on estettävien tai sallittujen verkkoalueiden luettelon sisältäviä tiedostoja:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>jne.<br/></p><p> Yksi verkkoalue per rivi. Tyhjät tai #-alkuiset rivit jätetään huomiotta.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1028"/> + <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source> + <translation><html><head/><body><p>Valitse hakemisto, jossa on luetteloita estettävistä tai sallittavista verkkotunnuksista.</p><p>Laita kyseiseen hakemistoon minkä tahansa tiedostopäätteen omaavia tiedostoja, jotka sisältävät luetteloita verkkotunnuksista.</p><p><br/> Luettelon jokaisen merkinnän muoto on seuraava (hosts-muodossa):</p><p>127.0.0.1 www.domain.com</p><p>tai </p><p>0.0.0.0.0 www.domain.com</p><p>Tyhjiä rivejä tai rivejä, jotka alkavat merkinnällä #, ei huomioida.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1043"/> + <source>To this list of domains +(regular expressions)</source> + <translation>Tähän verkkotunnusten luetteloon +(säännölliset lausekkeet)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1070"/> + <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation><html><head/><body><p>Valitse hakemisto, jossa on tiedostoja, jotka sisältävät säännöllisiä lausekkeita estettävistä tai sallittavista verkkotunnuksista:</p><p>.*\.example\.com</p><p>Voit myös käyttää verkkotunnusta sellaisenaan: &quot;example.com&quot;, jolloin se vastaa whatever.example.com, whatever.example.com.localdomain jne.</p><p>Yksi verkkotunnus riviä kohti. Tyhjät tai #-alkuiset rivit jätetään huomiotta.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1080"/> + <source>More</source> + <translation>Lisää</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1086"/> + <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source> + <translation><html><head/><body><p>Oletusarvoisesti sääntöjen kentässä ei oteta huomioon isoja ja pieniä kirjaimia, eli jos prosessi yrittää käyttää gOOgle.CoM:ää ja sinulla on sääntö Deny .*google.com, yhteys estetään.<br/></p><p> Jos ruksaat tämän ruudun, sinun on määritettävä tarkka merkkijono (verkkotunnus, suoritettava ohjelma, komentorivi), jonka haluat suodattaa.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1089"/> + <source>Case-sensitive</source> + <translation>Kirjainkoolla on merkitystä</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1096"/> + <source>Don't log connections that match this rule</source> + <translation>Älä logita yhteyksiä, jotka vastaavat tätä sääntöä</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1099"/> + <source>Don't log connections</source> + <translation>Älä logita yhteyksiä</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1145"/> + <source>Description...</source> + <translation>Kuvaus...</translation> + </message> +</context> +<context> + <name>StatsDialog</name> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="34"/> + <source>OpenSnitch Network Statistics</source> + <translation>OpenSnitch -verkkotilastot</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="94"/> + <source>Filter</source> + <translation>Suodatin</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1793"/> + <source>-</source> + <translation>-</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="107"/> + <source>Allow</source> + <translation>Salli</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="116"/> + <source>Deny</source> + <translation>Estä</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="125"/> + <source>Reject</source> + <translation>Hylkää</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="143"/> + <source>Ex.: firefox</source> + <translation>Esim.: firefox</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="180"/> + <source>0</source> + <translation>0</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="205"/> + <source>50</source> + <translation>50</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="210"/> + <source>100</source> + <translation>100</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="215"/> + <source>200</source> + <translation>200</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="220"/> + <source>300</source> + <translation>300</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="233"/> + <source>Delete all intercepted events</source> + <translation>Poista kaikki kaapatut tapahtumat</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="333"/> + <source>Create a new rule</source> + <translation>Luo uusi sääntö</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="376"/> + <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source> + <translation><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1.1</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="413"/> + <source>Status</source> + <translation>Tila</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="451"/> + <source>Start or Stop interception</source> + <translation>Aloita tai lopeta kaappaus</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="496"/> + <source>Events</source> + <translation>Tapahtumat</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="826"/> + <source>Nodes</source> + <translation>Solmut</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="637"/> + <source>Delete this node</source> + <translation>Poista tämä solmu</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="653"/> + <source>Show the preferences of this node</source> + <translation>Näytä tämän solmun asetukset</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="669"/> + <source>Start or stop interception of this node</source> + <translation>Tämän solmun kuuntelun aloittaminen tai lopettaminen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1700"/> + <source>Rules</source> + <translation>Säännöt</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="777"/> + <source>2</source> + <translation>2</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="782"/> + <source>Application rules</source> + <translation>Sovellussäännöt</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="936"/> + <source>Permanent</source> + <translation>Pysyvä</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="945"/> + <source>Temporary</source> + <translation>Väliaikainen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="954"/> + <source>System rules</source> + <translation>Järjestelmän säännöt</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="927"/> + <source>All applications</source> + <translation>Kaikki sovellukset</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="995"/> + <source>enable</source> + <translation>ota käyttöön</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1022"/> + <source>Edit rule</source> + <translation>Muokkaa sääntöä</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1039"/> + <source>Delete rule</source> + <translation>Poista sääntö</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1063"/> + <source>Hosts</source> + <translation>Isännät</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1153"/> + <source>Applications</source> + <translation>Sovellukset</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1266"/> + <source>Addresses</source> + <translation>Osoitteet</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1356"/> + <source>Ports</source> + <translation>Portit</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1440"/> + <source>Users</source> + <translation>Käyttäjät</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1544"/> + <source>Connections</source> + <translation>Yhteydet</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1596"/> + <source>Dropped</source> + <translation>Pudotetut</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1648"/> + <source>Uptime</source> + <translation>Käynnissäoloaika</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1767"/> + <source>Version</source> + <translation>Versio</translation> + </message> +</context> +<context> + <name>contextual_menu</name> + <message> + <location filename="../../../opensnitch/service.py" line="47"/> + <source>Statistics</source> + <translation>Tilastot</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="48"/> + <source>Enable</source> + <translation>Ota käyttöön</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="49"/> + <source>Disable</source> + <translation>Poista käytöstä</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="50"/> + <source>Help</source> + <translation>Apua</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="51"/> + <source>Close</source> + <translation>Sulje</translation> + </message> +</context> +<context> + <name>firewall</name> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="91"/> + <source>Configuration applied.</source> + <translation>Asetukset toteutettu.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="404"/> + <source>Error: {0}</source> + <translation>Virhe: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="193"/> + <source>Applying changes...</source> + <translation>Toteutetaan muutoksia...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="230"/> + <source>Error getting INPUT chain policy</source> + <translation>Virhe INPUT-ketjun käytännön saamisessa</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="237"/> + <source>Error getting OUTPUT chain policy</source> + <translation>Virhe OUTPUT-ketjun käytännön saamisessa</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="290"/> + <source>In order to configure firewall rules from the GUI, we need to use 'nftables' instead of 'iptables'</source> + <translation>Jotta voimme määrittää palomuurisääntöjä käyttöliittymästä, meidän on käytettävä 'nftables'-ohjelmaa 'iptables'-ohjelman sijasta</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="304"/> + <source>Enabling firewall...</source> + <translation>Otetaan käyttöön palomuuria...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="306"/> + <source>Disabling firewall...</source> + <translation>Otetaan palomuuria pois käytöstä...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="71"/> + <source>Dest Port</source> + <translation>Kohdeportti</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="72"/> + <source>Source Port</source> + <translation>Lähdeportti</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="73"/> + <source>Dest IP</source> + <translation>Kohde-IP</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="74"/> + <source>Source IP</source> + <translation>Lähde-IP</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="75"/> + <source>Input interface</source> + <translation>Tuloliitäntä</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="76"/> + <source>Output interface</source> + <translation>Lähtöliitäntä</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="77"/> + <source>Set conntrack mark</source> + <translation>Aseta conntrack-merkki</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="78"/> + <source>Match conntrack mark</source> + <translation>Kohdista conntrack-merkki</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="79"/> + <source>Match conntrack state(s)</source> + <translation>Kohdista conntrack-tila(t)</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="80"/> + <source>Set mark on packet</source> + <translation>Aseta merkki pakettiin</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="81"/> + <source>Match packet information</source> + <translation>Kohdista pakettitiedot</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="87"/> + <source>Bandwidth quotas</source> + <translation>Kaistanleveyskiintiöt</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="89"/> + <source>Rate limit connections</source> + <translation>Nopeusrajoita yhteyksiä</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="108"/> + <source> +Supported formats: + + - Simple: 23 + - Ranges: 80-1024 + - Multiple ports: 80,443,8080 +</source> + <translation> +Tuetut formaatit: + + - 23 + - Alueet: 80-1024 + - Useita portteja: 80,443,8080 +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="134"/> + <source> +Supported formats: + + - Simple: 1.2.3.4 + - IP ranges: 1.2.3.100-1.2.3.200 + - Network ranges: 1.2.3.4/24 +</source> + <translation> +Tuetut formaatit: + + - 1.2.3.4 + - IP-alueet: 1.2.3.100-1.2.3.200 + - Verkkoalueet: 1.2.3.4/24 +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="147"/> + <source>Match input interface. Regular expressions not allowed.</source> + <translation>Kohdista tuloliitäntä. Säännölliset lausekkeet eivät ole sallittuja.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="154"/> + <source>Match output interface. Regular expressions not allowed.</source> + <translation>Sovita lähtöliitäntä. Säännölliset lausekkeet eivät ole sallittuja.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="161"/> + <source>Set a conntrack mark on the connection, in decimal format.</source> + <translation>Asettaa yhteyden conntrack-merkki desimaalimuodossa.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="171"/> + <source>Match a conntrack mark of the connection, in decimal format.</source> + <translation>Kohdista yhteyden conntrack-merkki, desimaalimuodossa.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="178"/> + <source>Match conntrack states. + +Supported formats: + - Simple: new + - Multiple states separated by commas: related,new +</source> + <translation>Kohdista conntrack-tilat. + +Tuetut formaatit: + - Yksinkertainen: new + - Useita tiloja pilkulla erotettuna: related,new +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="193"/> + <source> +Match packet's metainformation. + +Value must be in decimal format, except for the "l4proto" option. +For l4proto it can be a lower case string, for example: + tcp + udp + icmp, + etc + +If the value is decimal for protocol or lproto, it'll use it as the code of +that protocol. +</source> + <translation> +Match-paketin metatiedot. + +Arvon on oltava desimaalimuodossa, paitsi "l4proto"-vaihtoehdon tapauksessa. +l4proto voi olla esimerkiksi pienellä alkukirjaimella kirjoitettu merkkijono: + tcp + udp + icmp, + jne + +Jos protokollan tai lproton arvo on desimaalinen, se käyttää sitä koodina, joka on +protokollan koodina. +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="213"/> + <source>Set a mark on the packet matching the specified conditions. The value is in decimal format.</source> + <translation>Asettaa paketille merkki, joka vastaa määritettyjä ehtoja. Arvo on desimaalimuodossa.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="221"/> + <source> +Match ICMP codes. + +Supported formats: + - Simple: echo-request + - Multiple separated by commas: echo-request,echo-reply +</source> + <translation> +Kohdista ICMP-koodit. + +Tuetut muodot: + - Yksinkertainen: echo-request + - Useita pilkulla erotettuna: echo-request,echo-reply +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="234"/> + <source> +Match ICMPv6 codes. + +Supported formats: + - Simple: echo-request + - Multiple separated by commas: echo-request,echo-reply +</source> + <translation> +Kohista ICMPv6-koodit. + +Tuetut muodot: + - Yksinkertainen: echo-request + - Useita pilkulla erotettuna: echo-request,echo-reply +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="247"/> + <source>Print a message when this rule matches a packet.</source> + <translation>Tulostaa viestin, kun tämä sääntö vastaa pakettia.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="254"/> + <source> +Apply quotas on connections. + +For example when: + - "quota over 10/mbytes" -> apply the Action defined (DROP) + - "quota until 10/mbytes" -> apply the Action defined (ACCEPT) + +The value must be in the format: VALUE/UNITS, for example: + - 10mbytes, 1/gbytes, etc +</source> + <translation> +Sovelletaan kiintiöitä yhteyksiin. + +Esimerkiksi kun: + - Sovelletaan määriteltyä toimintoa (DROP), esimerkiksi: "kiintiö yli 10 megatavua". + - "kiintiö enintään 10 megatavua" -> sovelletaan määriteltyä toimintoa (ACCEPT). + +Arvon on oltava muotoa: VALUE/UNITS, esimerkiksi: + - 10mbytes, 1/gbytes, jne +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="286"/> + <source> +Apply limits on connections. + +For example when: + - "limit over 10/mbytes/minute" -> apply the Action defined (DROP, ACCEPT, etc) + (When there're more than 10MB per minute, apply an Action) + + - "limit until 10/mbytes/hour" -> apply the Action defined (ACCEPT) + +The value must be in the format: VALUE/UNITS/TIME, for example: + - 10/mbytes/minute, 1/gbytes/hour, etc +</source> + <translation> +Rajoita yhteyksiä. + +Esimerkiksi kun: + - Sovelletaan määriteltyä toimintoa (DROP, ACCEPT jne.). + (Kun yhteyksiä on yli 10 Mt minuutissa, sovelletaan toimintoa). + + - "rajoitus enintään 10 megatavua/tunti" -> sovelletaan määriteltyä toimintoa (ACCEPT). + +Arvon on oltava muotoa: VALUE/UNITS/TIME, esimerkiksi: + - 10/mbytes/minute, 1/gbytes/hour, jne +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="373"/> + <source>Your protobuf version is incompatible, you need to install protobuf 3.8.0 or superior +(pip3 install --ignore-installed protobuf==3.8.0)</source> + <translation>Protobuf-versiosi ei ole yhteensopiva, sinun on asennettava protobuf 3.8.0 tai uudempi versio. +(pip3 install --ignore-installed protobuf==3.8.0)</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="397"/> + <source>Rule deleted</source> + <translation>Sääntö poistettu</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="401"/> + <source>Rule added</source> + <translation>Sääntö lisätty</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="420"/> + <source>You can use ',' or '-' to specify multiple ports/IPs or ranges/values:<br><br>ports: 22 or 22,443 or 50000-60000<br>IPs: 192.168.1.1 or 192.168.1.30-192.168.1.130<br>Values: echo-reply,echo-request<br>Values: new,established,related</source> + <translation>Voit käyttää ',' tai '-' -merkkejä määrittääksesi useita portteja/IP-osoitteita tai alueita/arvoja:<br><br>ports: 22 tai 22,443 tai 50000-60000<br>IP:t: 192.168.1.1 tai 192.168.1.30-192.168.1.130<br>arvot: echo-reply,echo-request<br>arvot: new,established,related</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="440"/> + <source>Deleting rule, wait</source> + <translation>Poistetaan sääntöä, odota</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="443"/> + <source>Error updating rule</source> + <translation>Virhe säännön päivittämisessä</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="483"/> + <source>Adding rule, wait</source> + <translation>Lisäätään sääntöä, odota</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="492"/> + <source><select a statement></source> + <translation><valitse lausuma></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="607"/> + <source>num</source> + <translation>num</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="621"/> + <source>to</source> + <translation>kohteeseen</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="787"/> + <source>Equal</source> + <translation>Yhtä suuri</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="788"/> + <source>Not equal</source> + <translation>Ei yhtäläinen</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="789"/> + <source>Greater or equal than</source> + <translation>Suurempi tai yhtä suuri kuin</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="790"/> + <source>Greater than</source> + <translation>Suurempi kuin</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="791"/> + <source>Less or equal than</source> + <translation>Pienempi tai yhtä suuri kuin</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="792"/> + <source>Less than</source> + <translation>Vähemmän kuin</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1350"/> + <source>Firewall rule</source> + <translation>Palomuurisääntö</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="885"/> + <source>Simple</source> + <translation>Yksinkertainen</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="890"/> + <source>Advanced</source> + <translation>Edistynyt</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1046"/> + <source>This rule is not supported yet.</source> + <translation>Tätä sääntöä ei vielä tueta.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1111"/> + <source>Exclude service</source> + <translation>Sulje palvelu pois</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1123"/> + <source>Allow inbound connections to the selected port.</source> + <translation>Salli saapuvat yhteydet valittuun porttiin.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1125"/> + <source>Allow outbound connections to the selected port.</source> + <translation>Salli lähtevät yhteydet valittuun porttiin.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1201"/> + <source>select a statement.</source> + <translation>valitse lausuma.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1217"/> + <source>value cannot be 0 or empty.</source> + <translation>arvo ei voi olla 0 tai tyhjä.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1229"/> + <source>the value format is 1024/kbytes (or bytes, mbytes, gbytes)</source> + <translation>arvomuoto on 1024/kbytes (tai bytes, mbytes, gbytes)</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1240"/> + <source>the value format is 1024/kbytes/second (or bytes, mbytes, gbytes)</source> + <translation>arvomuoto on 1024 kbytes/sekunti (tai bytes, mbytes, gbytes)</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1243"/> + <source>rate-limit not valid, use: bytes, kbytes, mbytes or gbytes.</source> + <translation>rajoitus ei kelpaa, käytä: bytes, kbytes, mbytes tai gbytes.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1245"/> + <source>time-limit not valid, use: second, minute, hour or day</source> + <translation>aikaraja ei ole voimassa, käytä: second, minute, hour tai day</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1293"/> + <source>port not valid.</source> + <translation>portti ei kelpaa.</translation> + </message> +</context> +<context> + <name>messages</name> + <message> + <location filename="../../../opensnitch/service.py" line="281"/> + <source>Info</source> + <translation>Tiedot</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="285"/> + <source>Error</source> + <translation>Virheet</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="289"/> + <source>Warning</source> + <translation>Varoitukset</translation> + </message> +</context> +<context> + <name>notifications</name> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="654"/> + <source>System notifications are not available, you need to install python3-notify2.</source> + <translation>Järjestelmäilmoitukset eivät ole käytettävissä, sinun on asennettava python3-notify2.</translation> + </message> +</context> +<context> + <name>popups</name> + <message> + <location filename="../../../opensnitch/notifications.py" line="42"/> + <source>Open</source> + <translation>Avaa</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="115"/> + <source>Allow</source> + <translation>Salli</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="114"/> + <source>Deny</source> + <translation>Estä</translation> + </message> + <message> + <location filename="../../../opensnitch/notifications.py" line="122"/> + <source>New outgoing connection</source> + <translation>Uusi lähtevä yhteys</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="490"/> + <source>is connecting to <b>%s</b> on %s port %d</source> + <translation>on yhdistämässä <b>%s</b> kohteen %s portissa %d</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="50"/> + <source>until reboot</source> + <translation>uudelleenkäynnistykseen asti</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/> + <source>forever</source> + <translation>ikuisesti</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="116"/> + <source>Reject</source> + <translation>Hylkää</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="331"/> + <source>Outgoing connection</source> + <translation>Lähtevä yhteys</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="336"/> + <source>Process launched from:</source> + <translation>Prosessi käynnistetty kohteesta:</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="373"/> + <source>from this executable</source> + <translation>tästä ohjelmatiedostosta</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="377"/> + <source>from this command line</source> + <translation>tästä komentorivistä</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="379"/> + <source>to port {0}</source> + <translation>porttiin {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="442"/> + <source>to {0}</source> + <translation>kohteeseen {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="382"/> + <source>from user {0}</source> + <translation>käyttäjältä {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="386"/> + <source>from this PID</source> + <translation>tästä PID:istä</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="399"/> + <source>to {0}.*</source> + <translation>kohteeseen {0}.*</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="452"/> + <source>to *.{0}</source> + <translation>kohteeseen *.{0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="486"/> + <source><b>Remote</b> process %s running on <b>%s</b></source> + <translation><b>Etä</b>prosessi %s on käynnissä kohteessa <b>%s</b></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="497"/> + <source>is connecting to <b>%s</b>, %s</source> + <translation>yhdistää kohteeseen <b>%s</b>, %s</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="502"/> + <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source> + <translation>yrittää selvittää <b>%s</b>%s, %s portti %d kautta</translation> + </message> +</context> +<context> + <name>preferences</name> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> + <source>Warning</source> + <translation>Varoitus</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="38"/> + <source>Restart the GUI in order effects to take effect</source> + <translation>Uudelleenkäynnistä käyttöliittymä uudelleen, jotta muutokset tulevat voimaan</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="388"/> + <source>There're no nodes connected</source> + <translation>Solmuja ei ole yhdistetty</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="164"/> + <source>System default</source> + <translation>Järjestelmän oletus</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="466"/> + <source>System</source> + <translation>Järjestelmä</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="187"/> + <source>Themes not available. Install qt-material: pip3 install qt-material</source> + <translation>Teemat eivät ole käytettävissä. Asenna qt-material: pip3 install qt-material</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="292"/> + <source>Server address can not be empty</source> + <translation>Palvelimen osoite ei voi olla tyhjä</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="321"/> + <source>Error loading {0} configuration</source> + <translation>Virhe asetuksen {0} lataamisessa</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="386"/> + <source>Exception saving config: {0}</source> + <translation>Poikkeus asetusten tallentamisessa: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="401"/> + <source>DB type changed</source> + <translation>Tietokantatyyppi muutettu</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> + <source>You must select a file for the database<br>or choose "In memory" type.</source> + <translation>Sinun on valittava tiedosto tietokannalle<br>tai valittava tyypiksi "Muistissa".</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="433"/> + <source>Language changed</source> + <translation>Kieli muuutettu</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> + <source>UI theme changed</source> + <translation>Käyttöliittymäteema muutettu</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> + <source>Restart the GUI in order to apply the new theme</source> + <translation>Uudelleenkäynnistä käyttöliittymä, jotta uusi teema tulee voimaan</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="500"/> + <source>Applying configuration on {0} ...</source> + <translation>Toteutetaan asetuksia {0} ...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="508"/> + <source>Ok</source> + <translation>Ok</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="520"/> + <source>Exception saving node config {0}: {1}</source> + <translation>Virhe solmun asetusten tallennuksessa {0}: {1}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="568"/> + <source>Configuration applied.</source> + <translation>Asetukset toteutettu.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="570"/> + <source>Error applying configuration: {0}</source> + <translation>Virhe asetusten toteuttamisessa: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="607"/> + <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source> + <translation>Vie hiiri tekstien päälle näyttääksesi ohjeen.<br><br>Älä unohda käydä wikissä: <a href="{0}">{0}</a></translation> + </message> +</context> +<context> + <name>proc_details</name> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="100"/> + <source><b>Error loading process information:</b> <br><br> + +</source> + <translation><b>Virhe prosessin tietojen lataamisessa:</b> <br><br> + +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="119"/> + <source><b>Error stopping monitoring process:</b><br><br></source> + <translation><b>Virhe prosessin monitoroinnin pysäyttämisessä:</b><br><br></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="159"/> + <source>loading...</source> + <translation>ladataan...</translation> + </message> +</context> +<context> + <name>rules</name> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="228"/> + <source>There're no nodes connected.</source> + <translation>Solmuja ei ole yhdistetty.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="245"/> + <source>There's already a rule with this name.</source> + <translation>Tällä nimellä on jo sääntö.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="271"/> + <source>Rule applied.</source> + <translation>Sovellettu sääntö.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="273"/> + <source>Error applying rule: {0}</source> + <translation>Virhe säännön soveltamisessa: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="539"/> + <source><b>Error loading rule</b></source> + <translation><b>Virhe säännön lataamisessa</b></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="641"/> + <source>protocol can not be empty, or uncheck it</source> + <translation>protokolla ei voi olla tyhjä, tai poista valintaruutu</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="655"/> + <source>Protocol regexp error</source> + <translation>Protokollan regexp-virhe</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="659"/> + <source>process path can not be empty</source> + <translation>prosessipolku ei voi olla tyhjä</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="673"/> + <source>Process path regexp error</source> + <translation>Prosessin polun regexp-virhe</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="677"/> + <source>command line can not be empty</source> + <translation>komentorivi ei voi olla tyhjä</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="691"/> + <source>Command line regexp error</source> + <translation>Komentorivin regexp-virhe</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="695"/> + <source>Network interface can not be empty</source> + <translation>Verkkoliitäntä ei voi olla tyhjä</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="709"/> + <source>Network interface regexp error</source> + <translation>Verkkoliitännän regexp-virhe</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="713"/> + <source>Source port can not be empty</source> + <translation>Lähdeportti ei voi olla tyhjä</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="727"/> + <source>Source port regexp error</source> + <translation>Lähdeportin regexp-virhe</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="731"/> + <source>Dest port can not be empty</source> + <translation>Kohdeportti ei voi olla tyhjä</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="745"/> + <source>Dst port regexp error</source> + <translation>Kohdeportin regexp-virhe</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="749"/> + <source>Dest host can not be empty</source> + <translation>Kohdeisäntä ei voi olla tyhjä</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="763"/> + <source>Dst host regexp error</source> + <translation>Kohdeisännän regexp-virhe</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="767"/> + <source>Source IP/Network can not be empty</source> + <translation>Lähde-IP/-verkko ei voi olla tyhjä</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="793"/> + <source>Source IP regexp error</source> + <translation>Lähde-IP:n regexp-virhe</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="805"/> + <source>Dest IP/Network can not be empty</source> + <translation>Kohde-IP/-verkko ei voi olla tyhjä</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="831"/> + <source>Dst IP regexp error</source> + <translation>Kohde-IP:n regexp-virhe</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="843"/> + <source>User ID can not be empty</source> + <translation>Käyttäjä-ID ei voi olla tyhjä</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="857"/> + <source>User ID regexp error</source> + <translation>Käyttäjä-ID:n regexp-virhe</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="861"/> + <source>PID field can not be empty</source> + <translation>PID-kenttä ei voi olla tyhjä</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="875"/> + <source>PID field regexp error</source> + <translation>PID-kentän regexp-virhe</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="931"/> + <source>Lists field cannot be empty</source> + <translation>Listat-kenttä ei voi olla tyhjä</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="933"/> + <source>Lists field must be a directory</source> + <translation>Listat-kentän on oltava hakemisto</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="963"/> + <source>Select at least one field.</source> + <translation>Valitse vähintään yksi kenttä.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="976"/> + <source><b>Rule not supported</b></source> + <translation><b>Sääntö ei tuettu</b></translation> + </message> +</context> +<context> + <name>stats</name> + <message> + <location filename="../../../opensnitch/service.py" line="211"/> + <source>WARNING</source> + <translation>VAROITUS</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="776"/> + <source>New node connected</source> + <translation>Uusi solmu kytketty</translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="17"/> + <source>What</source> + <translation>Mikä</translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="18"/> + <source>Hits</source> + <translation>Osumia</translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="19"/> + <source>Network name</source> + <translation>Verkon nimi</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Time</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Aika</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> + <source>Node</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Solmu</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> + <source>Action</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Toiminto</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>Destination</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Kohde</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> + <source>Protocol</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Protokolla</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Process</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Prosessi</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> + <source>Rule</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Sääntö</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="287"/> + <source>Name</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Nimi</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="288"/> + <source>Address</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Osoite</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="289"/> + <source>Status</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Tila</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="290"/> + <source>Hostname</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Isäntänimi</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="291"/> + <source>Uptime</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Käynnissäoloaika</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="423"/> + <source>Version</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Versio</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="420"/> + <source>Rules</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Säännöt</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> + <source>Duration</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Kesto</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> + <source>Description</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Kuvaus</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Enabled</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Käytössä</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> + <source>Precedence</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Ensisijaisuus</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="438"/> + <source>Hits</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Osumia</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> + <source>Cmdline</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Komentorivi</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> + <source>DstIP</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Kohde-IP</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> + <source>DstHost</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Kohdeisäntä</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> + <source>DstPort</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Kohdeportti</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> + <source>UserID</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Käyttäjä-ID</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> + <source>LastConnection</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Viimeinen yhteys</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="313"/> + <source>Not running</source> + <translation>Ei käynnissä</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="314"/> + <source>Disabled</source> + <translation>Poissa käytöstä</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="315"/> + <source>Running</source> + <translation>Käynnissä</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="406"/> + <source>Export rules</source> + <translation>Vientisäännöt</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="407"/> + <source>Import rules</source> + <translation>Tuontisäännöt</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="408"/> + <source>Export events to CSV</source> + <translation>Vie tapahtumat CSV-tiedostoon</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> + <source>Quit</source> + <translation>Lopeta</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="421"/> + <source>Connections</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Yhteydet</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="422"/> + <source>Dropped</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Pudotetut</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="437"/> + <source>What</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Mikä</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="636"/> + <source>OpenSnitch Network Statistics {0}</source> + <translation>OpenSnitch-verkkotilastot {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="638"/> + <source>OpenSnitch Network Statistics for {0}</source> + <translation>OpenSnitch-verkkotilastot {0}:lle</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="833"/> + <source>Details</source> + <translation>Yksityiskohdat</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="834"/> + <source>Rules</source> + <translation>Säännöt</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="835"/> + <source>New</source> + <translation>Uusi</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="875"/> + <source>Action</source> + <translation>Toiminto</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="954"/> + <source>Disable</source> + <translation>Poista käytöstä</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="956"/> + <source>Enable</source> + <translation>Ota käyttöön</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="961"/> + <source>Delete</source> + <translation>Poista</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="960"/> + <source>Edit</source> + <translation>Muokkaa</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="931"/> + <source>Apply to</source> + <translation>Hae kohteeseen</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="932"/> + <source>Export</source> + <translation>Vie</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="940"/> + <source>Allow</source> + <translation>Salli</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="941"/> + <source>Deny</source> + <translation>Estä</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="942"/> + <source>Reject</source> + <translation>Hylkää</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="945"/> + <source>Always</source> + <translation>Aina</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="946"/> + <source>Until reboot</source> + <translation>Uudelleenkäynnistykseen asti</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="959"/> + <source>Duplicate</source> + <translation>Kaksoiskappaleet</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="964"/> + <source>To clipboard</source> + <translation>Leikepöydälle</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="965"/> + <source>To disk</source> + <translation>Levylle</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> + <source> Are you sure?</source> + <translation> Oletko varma?</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2523"/> + <source>Select a directory to export rules</source> + <translation>Valitse hakemisto, johon säännöt viedään</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1189"/> + <source> Your are about to delete this rule. </source> + <translation> Olet poistamassa tätä sääntöä. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1191"/> + <source> Your are about to delete this entry. </source> + <translation> Olet poistamassa tätä merkintää. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1248"/> + <source>Rule not found by that name and node</source> + <translation>Sääntöä ei löydy kyseisellä nimellä ja solmulla</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1301"/> + <source><b>Error:</b><br><br></source> + <comment>{0}</comment> + <translation><b>Virhe:</b><br><br></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1308"/> + <source>Warning:</source> + <translation>Varoitus:</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1678"/> + <source> You are about to delete this node. </source> + <translation> Olet poistamassa tätä solmua. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1687"/> + <source><b>Error deleting node</b><br><br></source> + <comment>{0}</comment> + <translation><b>Virhe poistaessa solmua</b><br><br></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> + <source> You are about to delete this rule. </source> + <translation> Olet poistamassa tätä sääntöä. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2478"/> + <source>Error exporting rules</source> + <translation>Virhe vietäessä sääntöjä</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2552"/> + <source>Select a directory with rules to import (JSON files)</source> + <translation>Valitse hakemisto, jossa on tuotavia sääntöjä (JSON-tiedostot)</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2566"/> + <source>Rules imported fine</source> + <translation>Säännöt tuotu hyvin</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2581"/> + <source>Save as CSV</source> + <translation>Tallenna CSV:nä</translation> + </message> +</context> +</TS> diff --git a/ui/i18n/locales/fr_FR/opensnitch-fr_FR.ts b/ui/i18n/locales/fr_FR/opensnitch-fr_FR.ts new file mode 100644 index 0000000..5ac785e --- /dev/null +++ b/ui/i18n/locales/fr_FR/opensnitch-fr_FR.ts @@ -0,0 +1,3388 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="fr"> +<context> + <name>Dialog</name> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="34"/> + <source>opensnitch-qt</source> + <translation>opensnitch-qt</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="300"/> + <source>User ID</source> + <translation>ID utilisateur</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="334"/> + <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source> + <translation><html><head/><body><p><span style=" font-weight:600;">Exécuté depuis</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="647"/> + <source>TextLabel</source> + <translation>TextLabel</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="437"/> + <source>Source IP</source> + <translation>IP source</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="458"/> + <source>Process ID</source> + <translation>ID processus</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="601"/> + <source>Destination IP</source> + <translation>IP destination</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="622"/> + <source>Dst Port</source> + <translation>interface destination</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="702"/> + <source>from this executable</source> + <translation>depuis cet exécutable</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="707"/> + <source>from this command line</source> + <translation>depuis cette ligne de commande</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="712"/> + <source>this destination port</source> + <translation>vers cette interface</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="717"/> + <source>this user</source> + <translation>cet utilisateur</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="722"/> + <source>this destination ip</source> + <translation>vers cette IP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="751"/> + <source>once</source> + <translation>une seule fois</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="756"/> + <source>30s</source> + <translation type="unfinished">30s</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="761"/> + <source>5m</source> + <translation type="unfinished">5mn</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="766"/> + <source>15m</source> + <translation type="unfinished">15mn</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="771"/> + <source>30m</source> + <translation type="unfinished">30mn</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="776"/> + <source>1h</source> + <translation type="unfinished">1h</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="706"/> + <source>for this session</source> + <translation type="obsolete">durante esta sesión</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="786"/> + <source>forever</source> + <translation>définitivement</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="346"/> + <source>Deny</source> + <translation type="unfinished">Refuser</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="337"/> + <source>Allow</source> + <translation>Permettre</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="865"/> + <source>+</source> + <translation>+</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="781"/> + <source>until reboot</source> + <translation>jusqu'au redémarrage</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="727"/> + <source>from this PID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="809"/> + <source>action</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="14"/> + <source>Firewall</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="55"/> + <source><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="320"/> + <source>Inbound</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="313"/> + <source>Outbound</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="275"/> + <source>Profile</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="375"/> + <source>Allow inbound connections to a port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="378"/> + <source>Allow service (IN)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="397"/> + <source>Exclude outbound connections to a port from being intercepted</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="406"/> + <source>Allow service (OUT)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="426"/> + <source>New rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="431"/> + <source>Close</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="14"/> + <source>Firewall rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="26"/> + <source>Node</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="38"/> + <source>Enable</source> + <translation type="unfinished">Activer</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="50"/> + <source>Description</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="90"/> + <source>Simple</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="154"/> + <source>Add new condition</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="177"/> + <source>Remove selected condition</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="233"/> + <source>Direction</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="248"/> + <source>IN</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="257"/> + <source>OUT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="223"/> + <source>Action</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="285"/> + <source>ACCEPT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="294"/> + <source>DROP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="303"/> + <source>REJECT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="312"/> + <source>RETURN</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="442"/> + <source>Clear</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="453"/> + <source>Delete</source> + <translation type="unfinished">Effacer</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="464"/> + <source>Save</source> + <translation type="unfinished">Enregistrer</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="475"/> + <source>Add</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="266"/> + <source>FORWARD</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="271"/> + <source>PREROUTING</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="276"/> + <source>POSTROUTING</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="321"/> + <source>QUEUE</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="330"/> + <source>DNAT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="335"/> + <source>SNAT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="340"/> + <source>REDIRECT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="359"/> + <source>depending on the Action (i.e.: target), the syntaxis of the parameters will vary. +Some examples: + +QUEUE -> num 0 (or 1, 2, ...) +REDIRECT, TPROXY, DNAT, SNAT, MASQUERADE: + to :22 + to 192.168.1.254:8080 + to 192.168.1.254 + to 1024-2048 (masquerade)</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>PreferencesDialog</name> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="14"/> + <source>Preferences</source> + <translation>Préférences</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="484"/> + <source>UI</source> + <translation>IU</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="54"/> + <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p></body></html></source> + <translation type="obsolete">Este timeout es la cuenta atrás que aparece cuando se muestra una ventana emergente</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="466"/> + <source>Default timeout</source> + <translation>attente par défaut</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="340"/> + <source>Pop-up default duration</source> + <translation>attente dialogue par défaut</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="866"/> + <source>Default duration</source> + <translation>attente par défaut</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="162"/> + <source>Pop-up default action</source> + <translation type="obsolete">Acción por defecto de la ventana emergente</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="483"/> + <source>Default action</source> + <translation type="obsolete">Acción por defecto</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="323"/> + <source>Default target</source> + <translation>cible par défaut</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="179"/> + <source>center</source> + <translation>centré</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="184"/> + <source>top right</source> + <translation>en haut à droite</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="189"/> + <source>bottom right</source> + <translation>en bas à gauche</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="194"/> + <source>top left</source> + <translation>en haut à gauche</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="199"/> + <source>bottom left</source> + <translation>en bas à gauche</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="167"/> + <source>Prompt dialog default position on screen</source> + <translation type="obsolete">Posición por defecto</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="283"/> + <source>by executable</source> + <translation>par l'exécutable</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="288"/> + <source>by command line</source> + <translation>par la ligne de commande</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="293"/> + <source>by destination port</source> + <translation>par l'interface de destination</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="298"/> + <source>by destination ip</source> + <translation>par l'IP de destination</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="303"/> + <source>by user id</source> + <translation>par cet utilisateur</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="970"/> + <source>once</source> + <translation>une seule fois</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="219"/> + <source>30s</source> + <translation type="unfinished">30s</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="224"/> + <source>5m</source> + <translation type="unfinished">5m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="229"/> + <source>15m</source> + <translation type="unfinished">15m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="234"/> + <source>30m</source> + <translation type="unfinished">30m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="239"/> + <source>1h</source> + <translation type="unfinished">1h</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="240"/> + <source>for this session</source> + <translation type="obsolete">durante esta sesión</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="249"/> + <source>forever</source> + <translation>définitivement</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1012"/> + <source>deny</source> + <translation>Refuser</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1021"/> + <source>allow</source> + <translation>Autoriser</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="406"/> + <source>Disable pop-ups, only display an alert</source> + <translation type="obsolete">Pas de dialogue, montrer juste une alerte</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="823"/> + <source>Nodes</source> + <translation>noeuds</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="829"/> + <source>Process monitor method</source> + <translation>méthode de surveillance des processus</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="863"/> + <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source> + <translation><html><head/><body><p>La durée par défaut concerne le cas où aucun utilisateur n'est connecté.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="988"/> + <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source> + <translation><html><head/><body><p>Adresse du noeud.</p><p>Default: unix:///tmp/osui.sock (unix:// requise si c'est un socket Unix)</p><p>Peut aussi être une adresse IP avec son port: 127.0.0.1:50051</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="991"/> + <source>Address</source> + <translation>Adresse</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1131"/> + <source>Default log level</source> + <translation>détail noté dans le log par défaut</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1039"/> + <source>Version</source> + <translation>Version</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="902"/> + <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source> + <translation><html><head/><body><p>L'action par défaut se déclenche s'il n'y a pas d'utilisateur connecté.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="846"/> + <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source> + <translation><html><head/><body><p>fichier dans lequel enregistrer le log<br/></p><p>/dev/stdout imprime les logs dans le standard output.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="849"/> + <source>Log file</source> + <translation>Fichier log</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="578"/> + <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons.</p><p>The pop-up dialog will only contain information about the network connection.</p></body></html></source> + <translation type="obsolete">Si marcas esta opción, OpenSnitch te preguntará para Aceptar o Denegar conexiones que no tengan un PID asociado por diferentes razones. + +La ventana emergente sólo contendrá información relativa a la conexión. + +Nota: Estas conexiones no tienen por qué indicar que algo sospechoso está sucediendo. Simplemente +es que no hemos descubierto el PID (por ejemplo conexiones que no se originan en la máquina, o paquetes en mal estado).</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="581"/> + <source>Intercept Unknown Connections</source> + <translation type="obsolete">Interceptar conexiones desconocidas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="921"/> + <source>HostName</source> + <translation>nom d'hôte (host)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1090"/> + <source>unix:///tmp/osui.sock</source> + <translation>unix:///tmp/osui.sock</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="975"/> + <source>until restart</source> + <translation>jusqu'au redémarrage</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="980"/> + <source>always</source> + <translation>toujours</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1102"/> + <source>/var/log/opensnitchd.log</source> + <translation>/var/log/opensnitchd.log</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1107"/> + <source>/dev/stdout</source> + <translation>/dev/stdout</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="879"/> + <source>Apply configuration to all nodes</source> + <translation>appliquer la configuration à tous les noeuds</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1146"/> + <source>Database</source> + <translation>Base de données</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1181"/> + <source>In memory</source> + <translation>En mémoire</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1186"/> + <source>File</source> + <translation>Fichier</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1482"/> + <source>Close</source> + <translation>Fermer</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1493"/> + <source>Apply</source> + <translation>Appliquer</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1504"/> + <source>Save</source> + <translation>Enregistrer</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="244"/> + <source>until reboot</source> + <translation>jusqu'au redémarrage</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1200"/> + <source>Database type</source> + <translation>type de base de données</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1207"/> + <source>Select</source> + <translation>Choisir</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="83"/> + <source>Pop-ups default options</source> + <translation type="obsolete">Opciones por defecto de las ventanas emergentes</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="367"/> + <source>Pop-ups default position on screen</source> + <translation type="obsolete">Posición en pantalla</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="102"/> + <source><html><head/><body><p>The advanced view allows you to apply more filters on a connection</p><p>when a pop-up appears.</p></body></html></source> + <translation type="obsolete">La vista avanzada permite filtrar conexiones por más parámetros</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="359"/> + <source>Show advanced view by default</source> + <translation>Montrer par défaut la vue détaillée</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="653"/> + <source>Action</source> + <translation>Action</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="375"/> + <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source> + <translation><html><head/><body><p>Sélectionner pour que les dialogues s'ouvrent avec le détail avancé.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="343"/> + <source>Duration</source> + <translation>Durée</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="263"/> + <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source> + <translation><html><head/><body><p>Par défaut un nouveau dialogue en version simple propose de filtrer les connexions ou les applications sur une propriété de la connexion (exécutable, port, IP, etc).</p><p>Avec ces options, vous pouvez choisir plusieurs critères pour filtrer les connexions.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="266"/> + <source>Filter connections also by:</source> + <translation>Filtrer aussi les connexion par :</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="362"/> + <source>If checked, this field will be checked when a pop-up is displayed</source> + <translation type="obsolete">Si lo seleccionas, este campo se usará para filtrar las conexiones</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="81"/> + <source>User ID</source> + <translation>ID utilisateur</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="97"/> + <source>Destination port</source> + <translation>interface (port) de destination</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="113"/> + <source>Destination IP</source> + <translation>IP de destination</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="463"/> + <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source> + <translation><html><head/><body><p>Ctte durée est l'attente par défaut lorsqu'un dialogue est présenté.</p><p>Sans réponse au dialogue, les options par défaut sont appliquées.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="356"/> + <source>The advanced view allows you to easily select multiple fields to filter connections</source> + <translation>La vue avancée permet de choisir facileent plusieurs critères pour filtrer les connexions</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="110"/> + <source>If checked, this field will be selected when a pop-up is displayed</source> + <translation>cocher pour que ce champ soit préselectionné lorsqu'un dialogue est affiché</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="159"/> + <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source> + <translation><html><head/><body><p>Action par défaut du dialogue.</p><p>Lorsqu'un nouveau dialogue est affiché, cette action sera présélectionnée, et donc appliquée s'il n'y a pas de réponse après le délai par défaut.</p><p><br/></p><p>Pendant que le dialogue demande si on autorise ou non la connexion:</p><p>1. toute autre nouvelle demande de connexion est refusée.</p><p>2. les connexions déjà connues sont autorisée ou rejetées selon les règles définies par l'utilisateur.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="905"/> + <source>Default action when the GUI is disconnected</source> + <translation>Action par défaut lorsque l'interface graphique utilisateur n'est pas connectée</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1001"/> + <source>Debug invalid connections</source> + <translation>Noter (debug) les connexions invalides</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="39"/> + <source>Pop-ups</source> + <translation>Dialogues</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="64"/> + <source>Default options</source> + <translation>Options par défaut</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="330"/> + <source>Default position on screen</source> + <translation>Position par défaut sur l'écran</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="769"/> + <source>any temporary rules</source> + <translation>toute règle temporaire</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="487"/> + <source><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Lorsque cette option est choisie, les règles de la durée sélectionnée ne sont pas ajoutées à la liste des règles temporaires présentées dans l'interface graphique utilisateur.</p><p><br/></p><p>Les règles temporaires sont cependant toujours valides et peuvent être utilisées lors d'une demande d'autorisation /refus de connexion.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="490"/> + <source>Don't save rules of duration</source> + <translation type="obsolete">Ne pas enregistrer les règles de cette durée :</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="463"/> + <source>Show events columns</source> + <translation type="obsolete">Mostrar columnas de la pestaña Eventos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="589"/> + <source>Time</source> + <translation>Temps</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="669"/> + <source>Destination</source> + <translation>Destination</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="637"/> + <source>Protocol</source> + <translation>Protocole</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="685"/> + <source>Process</source> + <translation>Processus</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="605"/> + <source>Rule</source> + <translation>Règle</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="621"/> + <source>Node</source> + <translation>Noeud</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="723"/> + <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using wireguard.</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>cochez ceci pour recevoir une demande d'autorisation/refus sur les connexions qui n'ont pas de processus (PID) associé, généralement parce que la connexion est en mauvais état.</p><p>Le dialogue ne contiendra alors que l'information sur la connexion.</p><p>Il y a cependant des scénarios pour lesquels ces demandes sont valides, comme par exemple l'établissement d'un VPN utilisant wireguard.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="577"/> + <source>Events tab columns</source> + <translation>colonnes évènements</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="308"/> + <source>by PID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="473"/> + <source>Disable pop-ups, only display a notification</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="496"/> + <source>Desktop notifications</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="514"/> + <source>Use system notifications</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="530"/> + <source>Use Qt notifications</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="559"/> + <source>Test</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="998"/> + <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1294"/> + <source>minutes</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1326"/> + <source>Minutes between events purges</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1352"/> + <source>days</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1365"/> + <source>Maximum days of events to keep</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="147"/> + <source>reject</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="716"/> + <source>System</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="695"/> + <source>Command line</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="708"/> + <source>Theme</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="748"/> + <source>Rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="756"/> + <source>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI. + +Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="761"/> + <source>Don't save/Delete rules of duration</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="779"/> + <source>30s or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="784"/> + <source>5m or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="789"/> + <source>15m or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="794"/> + <source>30m or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="799"/> + <source>1h or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="724"/> + <source>Language</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>ProcessDetailsDialog</name> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="14"/> + <source>Process details</source> + <translation>Détail processus</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="61"/> + <source>loading...</source> + <translation>chargement...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="81"/> + <source>CWD: loading...</source> + <translation>chargement CWD...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="93"/> + <source>mem stats: loading...</source> + <translation>chargement statistiques mémoire...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="121"/> + <source>Status</source> + <translation>Etat</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="135"/> + <source>Open files</source> + <translation>Fichiers ouverts</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="149"/> + <source>I/O Statistics</source> + <translation>Statistiques entrées/sorties</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="163"/> + <source>Memory mapped files</source> + <translation>Fichiers mappés en mémoire</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="177"/> + <source>Stack</source> + <translation>Pile</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="191"/> + <source>Environment variables</source> + <translation>Variables d'environnement</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="210"/> + <source>Application pids</source> + <translation>PIDs application</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="240"/> + <source>Start or stop monitoring this process</source> + <translation>Démarrer ou arrêter la surveillance de ce processus</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="256"/> + <source>Close</source> + <translation>Fermer</translation> + </message> +</context> +<context> + <name>RulesDialog</name> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="20"/> + <source>Rule</source> + <translation>Règle</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> + <source>Node</source> + <translation>Noeud</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> + <source>Apply rule to all nodes</source> + <translation>Appliquer la règle à tous les noeuds</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="379"/> + <source>From this command line</source> + <translation>Depuis cette ligne de commande</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="472"/> + <source>From this executable</source> + <translation>Depuis cet exécutable</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="56"/> + <source>Action</source> + <translation>Action</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="138"/> + <source>/path/to/executable, .*/bin/executable[0-9\.]+$, ...</source> + <translation type="obsolete">/chemin/vers/executable, .*/bin/executable[0-9\.]+$, ...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="610"/> + <source>To this IP / Network</source> + <translation>vers cette IP / ce réseau</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="97"/> + <source>once</source> + <translation>une seule fois</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="797"/> + <source>30s</source> + <translation type="obsolete">30s</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="802"/> + <source>5m</source> + <translation type="obsolete">5m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="807"/> + <source>15m</source> + <translation type="obsolete">15m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="812"/> + <source>30m</source> + <translation type="obsolete">30m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="817"/> + <source>1h</source> + <translation type="obsolete">1h</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="230"/> + <source>until restart</source> + <translation type="obsolete">hasta reiniciar (el servicio)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="132"/> + <source>always</source> + <translation>toujours</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="902"/> + <source>To this port</source> + <translation>vers cette interface (port)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="372"/> + <source>From this user ID</source> + <translation>depuis cet utilisateur (ID)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/> + <source>Commas or spaces are not allowed to specify multiple domains. + +Use regular expressions instead: +.*(opensnitch|duckduckgo).com +.*\.google.com + +or a single domain: +www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... +gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source> + <translation>ni virgules ni espaces ne sont autorisés pour spécifier de multiples domaines. + +Utiliser à la place des 'expressions régulières' (RegExp): +.*(opensnitch|duckduckgo).com +.*\.google.com + +ou bien un seul domaine: +www.gnu.org - correspond uniquement à www.gnu.org, mais pas ftp.gnu.org, ou www2.gnu.org, ... +gnu.org - correspond uniquement à gnu.org, mais pas www.gnu.org, ou ftp.gnu.org, ...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="603"/> + <source>www.domain.org, .*\.domain.org</source> + <translation>www.domain.org, .*\.domain.org</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="526"/> + <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source> + <translation><html><head/><body><p>Seuls TCP, UDP ou UDPLITE sont permis</p><p>On peut employer des expressions régulières (regexp), par ex.: ^(TCP|UDP)$</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="532"/> + <source>TCP</source> + <translation>TCP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="338"/> + <source>UDP</source> + <translation type="obsolete">UDP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="343"/> + <source>UDPLITE</source> + <translation type="obsolete">UDPLITE</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="348"/> + <source>TCP6</source> + <translation type="obsolete">TCP6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="353"/> + <source>UDP6</source> + <translation type="obsolete">UDP6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="358"/> + <source>UDPLITE6</source> + <translation type="obsolete">UDPLITE6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="760"/> + <source>You can specify a single IP: +- 192.168.1.1 + +or a regular expression: +- 192\.168\.1\.[0-9]+ + +multiple IPs: +- ^(192\.168\.1\.1|172\.16\.0\.1)$ + +You can also specify a subnet: +- 192.168.1.0/24 + +Note: Commas or spaces are not allowed to separate IPs or networks.</source> + <translation>On peut spécifier une IP unique: +- 192.168.1.1 + +ou une "expression régulière": +- 192\.168\.1\.[0-9]+ +pluseurs IPs: +- ^(192\.168\.1\.1|172\.16\.0\.1)$ + +On peut aussi spécifier un sous-réseau: +- 192.168.1.0/24 + +Note : on ne peut pas utiliser virgules ou espaces pour séparer plusieurs IP ou réseaux.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="459"/> + <source>LAN</source> + <translation type="obsolete">LAN</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="464"/> + <source>127.0.0.0/8</source> + <translation type="obsolete">127.0.0.0/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="469"/> + <source>192.168.0.0/24</source> + <translation type="obsolete">192.168.0.0/24</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="474"/> + <source>192.168.1.0/24</source> + <translation type="obsolete">192.168.1.0/24</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> + <source>192.168.2.0/24</source> + <translation type="obsolete">192.168.2.0/24</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="484"/> + <source>192.168.0.0/16</source> + <translation type="obsolete">192.168.0.0/16</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="489"/> + <source>169.254.0.0/16</source> + <translation type="obsolete">169.254.0.0/16</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="494"/> + <source>172.16.0.0/12</source> + <translation type="obsolete">172.16.0.0/12</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="499"/> + <source>10.0.0.0/8</source> + <translation type="obsolete">10.0.0.0/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="504"/> + <source>::1/128</source> + <translation type="obsolete">::1/128</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="509"/> + <source>fc00::/7</source> + <translation type="obsolete">fc00::/7</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="514"/> + <source>ff00::/8</source> + <translation type="obsolete">ff00::/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="519"/> + <source>fe80::/10</source> + <translation type="obsolete">fe80::/10</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="524"/> + <source>fd00::/8</source> + <translation type="obsolete">fd00::/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="89"/> + <source>Duration</source> + <translation>Durée</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="633"/> + <source>Protocol</source> + <translation>Protocole</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="750"/> + <source>To this host</source> + <translation>Vers cet hôte</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="151"/> + <source>Deny</source> + <translation>Refuser</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="191"/> + <source>Allow</source> + <translation>Autoriser</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="245"/> + <source>Name</source> + <translation type="unfinished">Nom</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="207"/> + <source>Enable</source> + <translation>Activer</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="238"/> + <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them. + +000-allow-localhost +001-deny-broadcast +...</source> + <translation>Les règles étant appliquées par ordre aplhabétique, on peut leur donner priorité grâce à leur nom. + +000-allow-localhost +001-deny-broadcast +...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="773"/> + <source>leave blank to autocreate</source> + <translation type="obsolete">ne pas remplir va créer automatiquement</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="214"/> + <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one. + +You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example: + +[x] Priority - 000-priority-rule +[ ] Priority - 001-less-priority-rule</source> + <translation>Cocher pour que cette règle ait priorité sur toutes les autres. Aucune autre règle ne sera appliquée après celle-ci. + +Vous devez nommer la règle de façon qu'elle soit testée en premier, par ordre alphabétique. Par exemple: + +[x] Priority - 000-règle-prioritaire +[ ] Priority - 001-règle-moins-prioritaire</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="222"/> + <source>Priority rule</source> + <translation>Règle prioritaire</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1092"/> + <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source> + <translation><html><head/><body><p>Par défaut, le champ des règles n'est pas sensible à la casse. Par exemple si un processus tente d'atteindre gOOgle.CoM alors que vous avez une règle interdisant .*google.com, la connexion gOOgle.CoM sera bloquée.<br/></p><p>Si vous cochez ceci, vous devrez spécifier la chaîne exacte (domaine, exécutable, ligne de commande) que vous voulez filtrer.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1095"/> + <source>Case-sensitive</source> + <translation>sensible à la casse</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="686"/> + <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Vous pouvez spécifier plusieurs ports d'interface avec des "expessions régulières":</p><p><br/></p><p>- 53, 80 ou 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 ou 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="127"/> + <source>until reboot</source> + <translation>jusqu'au redémarrage</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="980"/> + <source>To this list of domains</source> + <translation>Vers cette liste de domaines</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="539"/> + <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Choisir un dossier contenant des listes de domaines à autoriser ou interdire.</p><p>Y mettre des fichiers contenant des listes de domaines, avec n'importe quelle extension.</p><p><br/>Le format de chaque entrée (hist format) est comme suit:</p><p>127.0.0.1 www.domain.com</p><p>ou </p><p>0.0.0.0 www.domain.com</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="346"/> + <source>Applications</source> + <translation type="unfinished">Applications</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="389"/> + <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="399"/> + <source>From this PID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="491"/> + <source>Network</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="932"/> + <source>List of domains/IPs</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="938"/> + <source>To this list of network ranges</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="945"/> + <source>To this list of IPs</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="971"/> + <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1006"/> + <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1034"/> + <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1049"/> + <source>To this list of domains +(regular expressions)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1076"/> + <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="168"/> + <source>Reject</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1151"/> + <source>Description...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="355"/> + <source><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="365"/> + <source>Is regular expression</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> + <source>is regular expression</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="863"/> + <source>Network interface</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1086"/> + <source>More</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1102"/> + <source>Don't log connections that match this rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1105"/> + <source>Don't log connections</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="148"/> + <source>Deny will just discard the connection</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="165"/> + <source>Reject will drop the connection, and kill the socket that initiated it</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="185"/> + <source>Allow will allow the connection</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="566"/> + <source>ICMP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="571"/> + <source>ICMP6</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="576"/> + <source>SCTP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="581"/> + <source>SCTP6</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="743"/> + <source>From this IP / Network</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="872"/> + <source>From this port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="918"/> + <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>StatsDialog</name> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="34"/> + <source>OpenSnitch Network Statistics</source> + <translation>Statistiques réseau Opensnitch</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="287"/> + <source>Save to CSV</source> + <translation type="obsolete">Enregistrer au format CSV.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="297"/> + <source>Ctrl+S</source> + <translation type="obsolete">contrôle-S</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="333"/> + <source>Create a new rule</source> + <translation>Créer une nouvelle règle</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="376"/> + <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source> + <translation><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="413"/> + <source>Status</source> + <translation>Etat</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1793"/> + <source>-</source> + <translation>-</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="451"/> + <source>Start or Stop interception</source> + <translation>Démarre ou stoppe l'interception</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="496"/> + <source>Events</source> + <translation>Evènements</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="94"/> + <source>Filter</source> + <translation>Filtre</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="107"/> + <source>Allow</source> + <translation>Autoriser</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="116"/> + <source>Deny</source> + <translation>Refuser</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="143"/> + <source>Ex.: firefox</source> + <translation>Ex.: firefox</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="205"/> + <source>50</source> + <translation>50</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="210"/> + <source>100</source> + <translation>100</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="215"/> + <source>200</source> + <translation>200</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="220"/> + <source>300</source> + <translation>300</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="826"/> + <source>Nodes</source> + <translation>Noeuds</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="554"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Addr column to view details of a node)</span></p></body></html></source> + <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(double cliquer sur la colonne Addr pour voir les détails d'un noeud)</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1700"/> + <source>Rules</source> + <translation>Règles</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="995"/> + <source>enable</source> + <translation>activer</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="684"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Name column to view details of a rule)</span></p></body></html></source> + <translation type="obsolete">(doble click en la columna Nombre para ver los detalles)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="692"/> + <source>search rule name</source> + <translation type="obsolete">rechercher par nom de règle</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="782"/> + <source>Application rules</source> + <translation>Règle d'applications</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="936"/> + <source>Permanent</source> + <translation>Permanent</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="945"/> + <source>Temporary</source> + <translation>Temporaire</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1063"/> + <source>Hosts</source> + <translation>Hôtes</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1364"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click to view details of an item)</span></p></body></html></source> + <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(double clic pour voir les détails d'un élément)</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1153"/> + <source>Applications</source> + <translation>Applications</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1266"/> + <source>Addresses</source> + <translation>Adresses</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1356"/> + <source>Ports</source> + <translation>Ports (interfaces)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1440"/> + <source>Users</source> + <translation>Utilisateurs</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1544"/> + <source>Connections</source> + <translation>Connexions</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1596"/> + <source>Dropped</source> + <translation>Abandonnée</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1648"/> + <source>Uptime</source> + <translation>durée active (uptime)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1767"/> + <source>Version</source> + <translation>Version</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="233"/> + <source>Delete all intercepted events</source> + <translation>Effacer tous les évènments interceptés</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1022"/> + <source>Edit rule</source> + <translation>Editer règle</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1039"/> + <source>Delete rule</source> + <translation>Effacer règle</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="926"/> + <source>Delete all intercepted hosts</source> + <translation type="obsolete">Effacer tous les hôtes interceptés</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1051"/> + <source>Delete all intercepted applications</source> + <translation type="obsolete">Effacer toutes les applications interceptées</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1159"/> + <source>Delete all intercepted addresses</source> + <translation type="obsolete">Effacer toutes les adresses interceptées</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1261"/> + <source>Delete all intercepted ports</source> + <translation type="obsolete">Effacer tous les ports interceptés</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1371"/> + <source>Delete all intercepted users</source> + <translation type="obsolete">Effacer tous les utilisateurs interceptés</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="699"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click on a row to view details of a rule)</span></p></body></html></source> + <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(double clic sur une ligne pour voir les détails d'une règle)</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="912"/> + <source>Delete connections that matched this rule</source> + <translation type="obsolete">Effacer les connexions qui déclenchaient cette règle</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="927"/> + <source>All applications</source> + <translation>Toutes les applications</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="125"/> + <source>Reject</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="180"/> + <source>0</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="777"/> + <source>2</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="954"/> + <source>System rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="637"/> + <source>Delete this node</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="653"/> + <source>Show the preferences of this node</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="669"/> + <source>Start or stop interception of this node</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>contextual_menu</name> + <message> + <location filename="../../../opensnitch/service.py" line="47"/> + <source>Statistics</source> + <translation>Statistiques</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="50"/> + <source>Help</source> + <translation>Aide</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="51"/> + <source>Close</source> + <translation>Fermer</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="48"/> + <source>Enable</source> + <translation>Activer</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="49"/> + <source>Disable</source> + <translation>Désactiver</translation> + </message> +</context> +<context> + <name>firewall</name> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="91"/> + <source>Configuration applied.</source> + <translation type="unfinished">Configuration appliquée.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="404"/> + <source>Error: {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="193"/> + <source>Applying changes...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="230"/> + <source>Error getting INPUT chain policy</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="237"/> + <source>Error getting OUTPUT chain policy</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="290"/> + <source>In order to configure firewall rules from the GUI, we need to use 'nftables' instead of 'iptables'</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="304"/> + <source>Enabling firewall...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="306"/> + <source>Disabling firewall...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="71"/> + <source>Dest Port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="72"/> + <source>Source Port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="73"/> + <source>Dest IP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="74"/> + <source>Source IP</source> + <translation type="unfinished">IP source</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="75"/> + <source>Input interface</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="76"/> + <source>Output interface</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="77"/> + <source>Set conntrack mark</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="78"/> + <source>Match conntrack mark</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="79"/> + <source>Match conntrack state(s)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="80"/> + <source>Set mark on packet</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="81"/> + <source>Match packet information</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="87"/> + <source>Bandwidth quotas</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="89"/> + <source>Rate limit connections</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="373"/> + <source>Your protobuf version is incompatible, you need to install protobuf 3.8.0 or superior +(pip3 install --ignore-installed protobuf==3.8.0)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="397"/> + <source>Rule deleted</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="401"/> + <source>Rule added</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="420"/> + <source>You can use ',' or '-' to specify multiple ports/IPs or ranges/values:<br><br>ports: 22 or 22,443 or 50000-60000<br>IPs: 192.168.1.1 or 192.168.1.30-192.168.1.130<br>Values: echo-reply,echo-request<br>Values: new,established,related</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="440"/> + <source>Deleting rule, wait</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="443"/> + <source>Error updating rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="483"/> + <source>Adding rule, wait</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="492"/> + <source><select a statement></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="787"/> + <source>Equal</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="788"/> + <source>Not equal</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="789"/> + <source>Greater or equal than</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="790"/> + <source>Greater than</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="791"/> + <source>Less or equal than</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="792"/> + <source>Less than</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1350"/> + <source>Firewall rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="885"/> + <source>Simple</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="890"/> + <source>Advanced</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1046"/> + <source>This rule is not supported yet.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1111"/> + <source>Exclude service</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1123"/> + <source>Allow inbound connections to the selected port.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1125"/> + <source>Allow outbound connections to the selected port.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1201"/> + <source>select a statement.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1217"/> + <source>value cannot be 0 or empty.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1229"/> + <source>the value format is 1024/kbytes (or bytes, mbytes, gbytes)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1240"/> + <source>the value format is 1024/kbytes/second (or bytes, mbytes, gbytes)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1243"/> + <source>rate-limit not valid, use: bytes, kbytes, mbytes or gbytes.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1245"/> + <source>time-limit not valid, use: second, minute, hour or day</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1293"/> + <source>port not valid.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="108"/> + <source> +Supported formats: + + - Simple: 23 + - Ranges: 80-1024 + - Multiple ports: 80,443,8080 +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="134"/> + <source> +Supported formats: + + - Simple: 1.2.3.4 + - IP ranges: 1.2.3.100-1.2.3.200 + - Network ranges: 1.2.3.4/24 +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="147"/> + <source>Match input interface. Regular expressions not allowed.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="154"/> + <source>Match output interface. Regular expressions not allowed.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="161"/> + <source>Set a conntrack mark on the connection, in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="171"/> + <source>Match a conntrack mark of the connection, in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="178"/> + <source>Match conntrack states. + +Supported formats: + - Simple: new + - Multiple states separated by commas: related,new +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="193"/> + <source> +Match packet's metainformation. + +Value must be in decimal format, except for the "l4proto" option. +For l4proto it can be a lower case string, for example: + tcp + udp + icmp, + etc + +If the value is decimal for protocol or lproto, it'll use it as the code of +that protocol. +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="213"/> + <source>Set a mark on the packet matching the specified conditions. The value is in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="221"/> + <source> +Match ICMP codes. + +Supported formats: + - Simple: echo-request + - Multiple separated by commas: echo-request,echo-reply +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="234"/> + <source> +Match ICMPv6 codes. + +Supported formats: + - Simple: echo-request + - Multiple separated by commas: echo-request,echo-reply +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="247"/> + <source>Print a message when this rule matches a packet.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="254"/> + <source> +Apply quotas on connections. + +For example when: + - "quota over 10/mbytes" -> apply the Action defined (DROP) + - "quota until 10/mbytes" -> apply the Action defined (ACCEPT) + +The value must be in the format: VALUE/UNITS, for example: + - 10mbytes, 1/gbytes, etc +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="286"/> + <source> +Apply limits on connections. + +For example when: + - "limit over 10/mbytes/minute" -> apply the Action defined (DROP, ACCEPT, etc) + (When there're more than 10MB per minute, apply an Action) + + - "limit until 10/mbytes/hour" -> apply the Action defined (ACCEPT) + +The value must be in the format: VALUE/UNITS/TIME, for example: + - 10/mbytes/minute, 1/gbytes/hour, etc +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="607"/> + <source>num</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="621"/> + <source>to</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>menu_close</name> + <message> + <location filename="../../../opensnitch/service.py" line="131"/> + <source>Close</source> + <translation type="obsolete">Cerrar</translation> + </message> +</context> +<context> + <name>menu_help</name> + <message> + <location filename="../../../opensnitch/service.py" line="126"/> + <source>Help</source> + <translation type="obsolete">Ayuda</translation> + </message> +</context> +<context> + <name>menu_statistics</name> + <message> + <location filename="../../../opensnitch/service.py" line="120"/> + <source>Statistics</source> + <translation type="obsolete">Eventos</translation> + </message> +</context> +<context> + <name>messages</name> + <message> + <location filename="../../../opensnitch/service.py" line="281"/> + <source>Info</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="285"/> + <source>Error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="289"/> + <source>Warning</source> + <translation type="unfinished">Alerte</translation> + </message> +</context> +<context> + <name>notifications</name> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="654"/> + <source>System notifications are not available, you need to install python3-notify2.</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>popups</name> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="115"/> + <source>Allow</source> + <translation>Autoriser</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="114"/> + <source>Deny</source> + <translation>Refuser</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/> + <source>forever</source> + <translation>indéfiniment</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="331"/> + <source>Outgoing connection</source> + <translation>Connexion sortante</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="336"/> + <source>Process launched from:</source> + <translation>Processus lancé par :</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="377"/> + <source>from this command line</source> + <translation>depuis cette ligne de commande</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="373"/> + <source>from this executable</source> + <translation>depuis cet exécutable</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="208"/> + <source>Unknown process</source> + <translation type="obsolete">Proceso no encontrado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="50"/> + <source>until reboot</source> + <translation>jusqu'au redémarrage</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="379"/> + <source>to port {0}</source> + <translation>vers le port {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="222"/> + <source><b>%s</b> is connecting to <b>%s</b> on %s port %d</source> + <translation type="obsolete"><b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="228"/> + <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source> + <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="442"/> + <source>to {0}</source> + <translation>vers {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="382"/> + <source>from user {0}</source> + <translation>de l'utilisateur {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="399"/> + <source>to {0}.*</source> + <translation>vers {0}.*</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="452"/> + <source>to *.{0}</source> + <translation>vers *.{0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="378"/> + <source>to *{0}</source> + <translation type="obsolete">vers *{0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="486"/> + <source><b>Remote</b> process %s running on <b>%s</b></source> + <translation>processus <b>Distant</b> %s tournant sur <b>%s</b></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="490"/> + <source>is connecting to <b>%s</b> on %s port %d</source> + <translation>se connecte à <b>%s</b> sur %s port %d</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="502"/> + <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source> + <translation>tente de résoudre (connecter) <b>%s</b> via %s, %s port %d</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="386"/> + <source>from this PID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/notifications.py" line="122"/> + <source>New outgoing connection</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="116"/> + <source>Reject</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="497"/> + <source>is connecting to <b>%s</b>, %s</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/notifications.py" line="42"/> + <source>Open</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>popups2</name> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="254"/> + <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source> + <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> + </message> +</context> +<context> + <name>preferences</name> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="171"/> + <source>Exception saving config: %s</source> + <translation type="obsolete">Error al guarda la configuración: %s</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="177"/> + <source>Applying configuration on %s ...</source> + <translation type="obsolete">Aplicando configuración en %s ...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="292"/> + <source>Server address can not be empty</source> + <translation>L'adresse du serveur ne peut être vide</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="227"/> + <source>Error loading %s configuration</source> + <translation type="obsolete">Error al cargar la configuración %s</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="568"/> + <source>Configuration applied.</source> + <translation>Configuration appliquée.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="257"/> + <source>Error applying configuration: %s</source> + <translation type="obsolete">Error al aplicar la configuración: %s</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="386"/> + <source>Exception saving config: {0}</source> + <translation>Config enregistrant les exceptions : {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="500"/> + <source>Applying configuration on {0} ...</source> + <translation>Applique la configuration à {0} ...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="321"/> + <source>Error loading {0} configuration</source> + <translation>Erreur au chargement configuration {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="570"/> + <source>Error applying configuration: {0}</source> + <translation>Erreur en tentant d'appliquer la configution : {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> + <source>Warning</source> + <translation>Alerte</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> + <source>You must select a file for the database<br>or choose "In memory" type.</source> + <translation>Vous devez sélectionner un fichier pour la base de données<br>ou choisir le type "en mémoire".</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="401"/> + <source>DB type changed</source> + <translation>type de base de données changé</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="38"/> + <source>Restart the GUI in order effects to take effect</source> + <translation>Prendra effet au redémarrage de l'interface graphique</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="607"/> + <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source> + <translation>Glisser la souris sur les textes pour afficher l'aide<br><br>N'hésitez pas à visiter le Wiki : <a href="{0}">{0}</a></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="466"/> + <source>System</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="187"/> + <source>Themes not available. Install qt-material: pip3 install qt-material</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> + <source>UI theme changed</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> + <source>Restart the GUI in order to apply the new theme</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="508"/> + <source>Ok</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="388"/> + <source>There're no nodes connected</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="520"/> + <source>Exception saving node config {0}: {1}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="164"/> + <source>System default</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="433"/> + <source>Language changed</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>proc_details</name> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="100"/> + <source><b>Error loading process information:</b> <br><br> + +</source> + <translation><b>Erreur au chargement d'information sur le processus:</b> <br><br> + +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="119"/> + <source><b>Error stopping monitoring process:</b><br><br></source> + <translation><b>Erreur en stoppant le processus de monitoring:</b><br><br></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="159"/> + <source>loading...</source> + <translation>chargement...</translation> + </message> +</context> +<context> + <name>rules</name> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="228"/> + <source>There're no nodes connected.</source> + <translation>Aucun noeud n'est connecté.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="271"/> + <source>Rule applied.</source> + <translation>Règle appliquée.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="123"/> + <source>Error applying rule: %s</source> + <translation type="obsolete">Error al aplicar la regla: %s</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="641"/> + <source>protocol can not be empty, or uncheck it</source> + <translation>le protocole ne peut être vide, ou bien le désélectionner</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="655"/> + <source>Protocol regexp error</source> + <translation>Erreur à l'interprétation du RegExp protocole</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="659"/> + <source>process path can not be empty</source> + <translation>le chemin vers le processus ne peut être vide</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="673"/> + <source>Process path regexp error</source> + <translation>erreur RegExp dans le chemin vers le processus</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="677"/> + <source>command line can not be empty</source> + <translation>la ligne de commande ne peut être vide</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="691"/> + <source>Command line regexp error</source> + <translation>Erreur RegExp dans la ligne de commande</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="731"/> + <source>Dest port can not be empty</source> + <translation>l'interface (port) destination ne peut être vide</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="745"/> + <source>Dst port regexp error</source> + <translation>ereur RegExp sur l'interface (port) de destination</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="749"/> + <source>Dest host can not be empty</source> + <translation>l'hôte de destination ne peut être vide</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="763"/> + <source>Dst host regexp error</source> + <translation>erreur RegExp sur l'hôte de destination</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="805"/> + <source>Dest IP/Network can not be empty</source> + <translation>l'IP / réseau de destination ne peut être vide</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="831"/> + <source>Dst IP regexp error</source> + <translation>erreur RegExp sur l'IP destination</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="843"/> + <source>User ID can not be empty</source> + <translation>l'ID utilisateur ne peut être vide</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="857"/> + <source>User ID regexp error</source> + <translation>erreur RegExp sur l'ID utilisateur</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="273"/> + <source>Error applying rule: {0}</source> + <translation>Erreur en appliquant la règle : {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="931"/> + <source>Lists field cannot be empty</source> + <translation>le champ "listes" ne peut être vide</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="933"/> + <source>Lists field must be a directory</source> + <translation>le champ Listes doit être un répertoire</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="976"/> + <source><b>Rule not supported</b></source> + <translation><b>RRègle non supportée</b></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="539"/> + <source><b>Error loading rule</b></source> + <translation><b>Erreur au chargement de la règle</b></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="245"/> + <source>There's already a rule with this name.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="861"/> + <source>PID field can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="875"/> + <source>PID field regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="963"/> + <source>Select at least one field.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="695"/> + <source>Network interface can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="709"/> + <source>Network interface regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="713"/> + <source>Source port can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="727"/> + <source>Source port regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="767"/> + <source>Source IP/Network can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="793"/> + <source>Source IP regexp error</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>stats</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="313"/> + <source>Not running</source> + <translation>Inactif</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="314"/> + <source>Disabled</source> + <translation>désactivé</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="315"/> + <source>Running</source> + <translation>Actif</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="412"/> + <source>OpenSnitch Network Statistics</source> + <translation type="obsolete">Eventos de OpenSnitch</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="414"/> + <source>OpenSnitch Network Statistics for</source> + <translation type="obsolete">Eventos de OpenSnitch de</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1189"/> + <source> Your are about to delete this rule. </source> + <translation> Vous êtes sur le point d'effacer cette règle. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> + <source> Are you sure?</source> + <translation> Êtes-vous sûr?</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="636"/> + <source>OpenSnitch Network Statistics {0}</source> + <translation>Statistiques réseau OpenSnitch {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="638"/> + <source>OpenSnitch Network Statistics for {0}</source> + <translation>Statistique réseau OpenSnitch pour {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> + <source>Name</source> + <translation type="obsolete">Nombre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Address</source> + <translation type="obsolete">Dirección</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="176"/> + <source>Status</source> + <translation type="obsolete">Estado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="177"/> + <source>Hostname</source> + <translation type="obsolete">Hostname</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="183"/> + <source>Version</source> + <translation type="obsolete">Versión</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="834"/> + <source>Rules</source> + <translation type="unfinished">Reglas</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Time</source> + <translation type="obsolete">Hora</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="875"/> + <source>Action</source> + <translation type="unfinished">Acción</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> + <source>Duration</source> + <translation type="obsolete">Duración</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> + <source>Node</source> + <translation type="obsolete">Nodo</translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="18"/> + <source>Hits</source> + <translation type="unfinished">Total</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>Protocol</source> + <translation type="obsolete">Protocolo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2581"/> + <source>Save as CSV</source> + <translation>Enregistrer en CSV</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Enabled</source> + <translation type="obsolete">Habilitado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="961"/> + <source>Delete</source> + <translation>Effacer</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="948"/> + <source>always</source> + <translation type="obsolete">siempre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="580"/> + <source><b>Error:</b><br><br>{0}</source> + <translation type="obsolete"><b>Error:</b><br><br>{0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="954"/> + <source>Disable</source> + <translation>Désactiver</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="956"/> + <source>Enable</source> + <translation>Activer</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="959"/> + <source>Duplicate</source> + <translation>Dupliquer</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="960"/> + <source>Edit</source> + <translation>Editer</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1248"/> + <source>Rule not found by that name and node</source> + <translation>Pas trouvé de règle pour ces nom et noeud</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1301"/> + <source><b>Error:</b><br><br></source> + <comment>{0}</comment> + <translation><b>Erreur:</b><br><br></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1308"/> + <source>Warning:</source> + <translation>Alerte :</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="940"/> + <source>Allow</source> + <translation>Autoriser</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="941"/> + <source>Deny</source> + <translation>Refuser</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="945"/> + <source>Always</source> + <translation>Toujours</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="946"/> + <source>Until reboot</source> + <translation>Jusqu'au redémarrage</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> + <source> You are about to delete this rule. </source> + <translation> Vous êtes sur le point d'effacer cette règle. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> + <source>Process</source> + <translation type="obsolete">Aplicación</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> + <source>Destination</source> + <translation type="obsolete">Destino</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> + <source>Rule</source> + <translation type="obsolete">Regla</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> + <source>UserID</source> + <translation type="obsolete">UserID</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="174"/> + <source>LastConnection</source> + <translation type="obsolete">Última Conexión</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> + <source>Name</source> + <comment>xxxxx</comment> + <translation type="obsolete">Nombre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> + <source>Name</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Nombre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Address</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Dirección</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> + <source>Status</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Estado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> + <source>Hostname</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Hostname</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> + <source>Version</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Versión</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> + <source>Rules</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Reglas</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Time</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Hora</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> + <source>Action</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Acción</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> + <source>Duration</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Duración</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> + <source>Node</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Nodo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Enabled</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Habilitado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> + <source>Hits</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Total</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>Protocol</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Protocolo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> + <source>Process</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Aplicación</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> + <source>Destination</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Destino</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> + <source>Rule</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Regla</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> + <source>UserID</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">UserID</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> + <source>LastConnection</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">ÚltimaConexión</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="287"/> + <source>Name</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Nom</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="288"/> + <source>Address</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Adresse</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="289"/> + <source>Status</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Etat</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="290"/> + <source>Hostname</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Nom d'hôte</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="423"/> + <source>Version</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Version</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="420"/> + <source>Rules</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Règles</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Time</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Temps</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> + <source>Action</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Action</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> + <source>Duration</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Durée</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> + <source>Node</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Noeud</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Enabled</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Activé</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="438"/> + <source>Hits</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Connexions</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> + <source>Protocol</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Protocole</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Process</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Processus</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>Destination</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Destination</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> + <source>Rule</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Règle</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> + <source>UserID</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>ID utilisateur</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> + <source>LastConnection</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>DernièreConnexion</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> + <source>Args</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="obsolete">Args</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> + <source>DstIP</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>DstIP</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> + <source>DstHost</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>DstHost</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> + <source>DstPort</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>DstPort</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="179"/> + <source>Uptime</source> + <translation type="obsolete">durée active (uptime)</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="181"/> + <source>Connections</source> + <translation type="obsolete">Connexions</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="182"/> + <source>Dropped</source> + <translation type="obsolete">Abandonnée</translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="17"/> + <source>What</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="931"/> + <source>Apply to</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="942"/> + <source>Reject</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="19"/> + <source>Network name</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="291"/> + <source>Uptime</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished">durée active (uptime)</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="421"/> + <source>Connections</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished">Connexions</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="422"/> + <source>Dropped</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished">Abandonnée</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="437"/> + <source>What</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> + <source>Precedence</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="776"/> + <source>New node connected</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> + <source>Description</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> + <source>Cmdline</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="406"/> + <source>Export rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="407"/> + <source>Import rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="408"/> + <source>Export events to CSV</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> + <source>Quit</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="932"/> + <source>Export</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="964"/> + <source>To clipboard</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="965"/> + <source>To disk</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2523"/> + <source>Select a directory to export rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1191"/> + <source> Your are about to delete this entry. </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1678"/> + <source> You are about to delete this node. </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1687"/> + <source><b>Error deleting node</b><br><br></source> + <comment>{0}</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2478"/> + <source>Error exporting rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2552"/> + <source>Select a directory with rules to import (JSON files)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2566"/> + <source>Rules imported fine</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="211"/> + <source>WARNING</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="833"/> + <source>Details</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="835"/> + <source>New</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>stats_deleterule</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="774"/> + <source> Your are about to delete this rule. </source> + <translation type="obsolete"> Estás a punto de borrar esta regla. </translation> + </message> +</context> +<context> + <name>stats_deleterule2</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="776"/> + <source> Are you sure?</source> + <translation type="obsolete"> ¿Estás seguro?</translation> + </message> +</context> +<context> + <name>stats_disabled</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="74"/> + <source>Disabled</source> + <translation type="obsolete">Deshabilitado</translation> + </message> +</context> +<context> + <name>stats_notrunning</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="73"/> + <source>Not running</source> + <translation type="obsolete">Parado</translation> + </message> +</context> +<context> + <name>stats_running</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="75"/> + <source>Running</source> + <translation type="obsolete">Interceptando</translation> + </message> +</context> +<context> + <name>stats_wintitle</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> + <source>OpenSnitch Network Statistics</source> + <translation type="obsolete">Eventos de red OpenSnitch</translation> + </message> +</context> +<context> + <name>stats_wintitle2</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="411"/> + <source>OpenSnitch Network Statistics for</source> + <translation type="obsolete">Eventos de OpenSnitch de</translation> + </message> +</context> +</TS> diff --git a/ui/i18n/locales/hu_HU/opensnitch-hu_HU.ts b/ui/i18n/locales/hu_HU/opensnitch-hu_HU.ts new file mode 100644 index 0000000..cea0fbe --- /dev/null +++ b/ui/i18n/locales/hu_HU/opensnitch-hu_HU.ts @@ -0,0 +1,3251 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="hu_HU"> +<context> + <name>Dialog</name> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="34"/> + <source>opensnitch-qt</source> + <translation>opensnitch-qt</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="150"/> + <source>Chromium Web Browser</source> + <translation type="obsolete">Chromium webböngésző</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="179"/> + <source><html><head/><body><p>/opt/google/chrome/bin/chrome --something abc --more-long def --for-word-wrapping</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>/opt/google/chrome/bin/chrome --valami ábécé --hosszabb-ideig def --szócsomagoláshoz</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="226"/> + <source>(/path/to/bin/chromium)</source> + <translation type="obsolete">(/út/a/bináris/Chromiumhoz)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="702"/> + <source>from this executable</source> + <translation>ettől a futtatható fájltól</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="707"/> + <source>from this command line</source> + <translation>erről a parancssorról</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="712"/> + <source>this destination port</source> + <translation>ezt célkikötőt</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="717"/> + <source>this user</source> + <translation>ezt a felhasználót</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="722"/> + <source>this destination ip</source> + <translation>ezt a cél IP címet</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="865"/> + <source>+</source> + <translation>+</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="751"/> + <source>once</source> + <translation>egyszer</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="756"/> + <source>30s</source> + <translation>30 másodperc</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="761"/> + <source>5m</source> + <translation>5 perc</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="766"/> + <source>15m</source> + <translation>15 perc</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="771"/> + <source>30m</source> + <translation>30 perc</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="776"/> + <source>1h</source> + <translation>1 óra</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="781"/> + <source>until reboot</source> + <translation>újraindításig</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="786"/> + <source>forever</source> + <translation>örökre</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="346"/> + <source>Deny</source> + <translation>Megtagadás</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="337"/> + <source>Allow</source> + <translation>Engedélyezés</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="300"/> + <source>User ID</source> + <translation>Felhasználói azonosító</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="334"/> + <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source> + <translation><html><head/><body><p><span style=" font-weight:600;">Futtatható fájl innen:</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="647"/> + <source>TextLabel</source> + <translation>Szövegcímke</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="437"/> + <source>Source IP</source> + <translation>Forrás IP-cím</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="458"/> + <source>Process ID</source> + <translation>Folyamatazonosító</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="601"/> + <source>Destination IP</source> + <translation>Cél IP-címe</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="622"/> + <source>Dst Port</source> + <translation>Célkikötő</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="271"/> + <source>Chromium Web Browser wants to connect to www.evilsocket.net on tcp port 443. And maybe to www.goodsocket.net on port 344</source> + <translation type="obsolete">A Chromium webböngésző csatlakozni akar a www.evilsocket.net webhelyhez a 443-as TCP porton. És talán a www.goodsocket.net webhelyhez a 344-es TCP porton</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="727"/> + <source>from this PID</source> + <translation>ebből a folyamatazonosítóból</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="809"/> + <source>action</source> + <translation>művelet</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="14"/> + <source>Firewall</source> + <translation>Tűzfal</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="55"/> + <source><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></source> + <translation><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Tűzfal</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="320"/> + <source>Inbound</source> + <translation>Bejövő</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="313"/> + <source>Outbound</source> + <translation>Kimenő</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="275"/> + <source>Profile</source> + <translation>Profil</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="375"/> + <source>Allow inbound connections to a port</source> + <translation>Bejövő kapcsolatok engedélyezése egy kikötőhöz</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="378"/> + <source>Allow service (IN)</source> + <translation>Szolgáltatás engedélyezése (BE)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="397"/> + <source>Exclude outbound connections to a port from being intercepted</source> + <translation>A kikötőhöz tartó kimenő kapcsolatok kizárása az elfogásból</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="406"/> + <source>Allow service (OUT)</source> + <translation>Szolgáltatás engedélyezése (KI)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="426"/> + <source>New rule</source> + <translation>Új szabály</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="453"/> + <source>xxx</source> + <translation type="obsolete">xxx</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="431"/> + <source>Close</source> + <translation>Bezárás</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="14"/> + <source>Firewall rule</source> + <translation>Tűzfalszabály</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="26"/> + <source>Node</source> + <translation>Csomópont</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="34"/> + <source>All</source> + <translation type="obsolete">Összes</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="38"/> + <source>Enable</source> + <translation>Engedélyezés</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="50"/> + <source>Description</source> + <translation>Leírás</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="90"/> + <source>Simple</source> + <translation>Egyszerű</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="154"/> + <source>Add new condition</source> + <translation>Új feltétel hozzáadása</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="177"/> + <source>Remove selected condition</source> + <translation>Kiválasztott feltétel eltávolítása</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="233"/> + <source>Direction</source> + <translation>Irány</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="248"/> + <source>IN</source> + <translation>BE</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="257"/> + <source>OUT</source> + <translation>KI</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="223"/> + <source>Action</source> + <translation>Művelet</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="285"/> + <source>ACCEPT</source> + <translation>ELFOGADÁS</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="294"/> + <source>DROP</source> + <translation>KIDOBÁS</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="303"/> + <source>REJECT</source> + <translation>ELUTASÍTÁS</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="312"/> + <source>RETURN</source> + <translation>VISSZATÉRÉS</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="442"/> + <source>Clear</source> + <translation>Kiürítés</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="453"/> + <source>Delete</source> + <translation>Törlés</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="464"/> + <source>Save</source> + <translation>Mentés</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="475"/> + <source>Add</source> + <translation>Hozzáadás</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="266"/> + <source>FORWARD</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="271"/> + <source>PREROUTING</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="276"/> + <source>POSTROUTING</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="321"/> + <source>QUEUE</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="330"/> + <source>DNAT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="335"/> + <source>SNAT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="340"/> + <source>REDIRECT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="359"/> + <source>depending on the Action (i.e.: target), the syntaxis of the parameters will vary. +Some examples: + +QUEUE -> num 0 (or 1, 2, ...) +REDIRECT, TPROXY, DNAT, SNAT, MASQUERADE: + to :22 + to 192.168.1.254:8080 + to 192.168.1.254 + to 1024-2048 (masquerade)</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>PreferencesDialog</name> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="14"/> + <source>Preferences</source> + <translation>Beállítások</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="484"/> + <source>UI</source> + <translation>Felhasználói felület</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="83"/> + <source>Pop-ups default options</source> + <translation type="obsolete">Az előugró ablakok alapértelmezett beállításai</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="367"/> + <source>Pop-ups default position on screen</source> + <translation type="obsolete">Felugró ablakok alapértelmezett helye a képernyőn</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="359"/> + <source>Show advanced view by default</source> + <translation>Alapértelmezés szerint a haladó nézet megjelenítése</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="970"/> + <source>once</source> + <translation>egyszer</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="219"/> + <source>30s</source> + <translation>30 másodperc</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="224"/> + <source>5m</source> + <translation>5 perc</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="229"/> + <source>15m</source> + <translation>15 perc</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="234"/> + <source>30m</source> + <translation>30 perc</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="239"/> + <source>1h</source> + <translation>1 óra</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="244"/> + <source>until reboot</source> + <translation>újraindításig</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="249"/> + <source>forever</source> + <translation>örökre</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="162"/> + <source>Pop-up default action</source> + <translation type="obsolete">Felugró ablak alapértelmezett művelete</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="653"/> + <source>Action</source> + <translation>Művelet</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="323"/> + <source>Default target</source> + <translation>Alapértelmezett cél</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="375"/> + <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source> + <translation><html><head/><body><p>Ha be van jelölve, az előugró ablakok aktív haladó nézettel jelennek meg.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1012"/> + <source>deny</source> + <translation>megtagadás</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1021"/> + <source>allow</source> + <translation>engedélyezés</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="283"/> + <source>by executable</source> + <translation>futtatható fájl szerint</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="288"/> + <source>by command line</source> + <translation>parancssor szerint</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="293"/> + <source>by destination port</source> + <translation>célkikötő szerint</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="298"/> + <source>by destination ip</source> + <translation>rendeltetési hely IP címe szerint</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="303"/> + <source>by user id</source> + <translation>felhasználói azonosító szerint</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="179"/> + <source>center</source> + <translation>középre</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="184"/> + <source>top right</source> + <translation>jobb felső</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="189"/> + <source>bottom right</source> + <translation>jobb alsó</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="194"/> + <source>top left</source> + <translation>bal felső</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="199"/> + <source>bottom left</source> + <translation>bal alsó</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="340"/> + <source>Pop-up default duration</source> + <translation>Előugró ablak alapértelmezett időtartama</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="343"/> + <source>Duration</source> + <translation>Időtartam</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="263"/> + <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source> + <translation><html><head/><body><p>Alapértelmezés szerint, amikor egy új előugró ablak jelenik meg, a legegyszerűbb formájában képes lesz a kapcsolatok vagy alkalmazások szűrésére a kapcsolat egy tulajdonságával (futtatható fájl, kikötő, IP-cím stb.).</p><p>Ezekkel az opciókkal több mezőt is választhat, amelyekhez a kapcsolatokat szűrni kívánja.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="266"/> + <source>Filter connections also by:</source> + <translation>Szűrje a csatlakozásokat az alábbiak szerint is:</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="81"/> + <source>User ID</source> + <translation>Felhasználói azonosító</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="97"/> + <source>Destination port</source> + <translation>Célkikötő</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="113"/> + <source>Destination IP</source> + <translation>Cél IP-címe</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="406"/> + <source>Disable pop-ups, only display an alert</source> + <translation type="obsolete">Tiltsa le a előugró elemet, csak riasztást jelenítsen meg</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="463"/> + <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source> + <translation><html><head/><body><p>Ez az időkorlát az a visszaszámlálás, amelyet akkor láthat, amikor egy felbukkanó párbeszédpanel jelenik meg.</p><p>Ha nem válaszol a felbukkanó párbeszédpanelre, akkor az alapértelmezett beállítások kerülnek alkalmazásra.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="466"/> + <source>Default timeout</source> + <translation>Alapértelmezett időtúllépés</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="823"/> + <source>Nodes</source> + <translation>Csomópontok</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="829"/> + <source>Process monitor method</source> + <translation>Folyamatfigyelés módszer</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="846"/> + <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source> + <translation><html><head/><body><p>Naplófájl a naplók írásához.<br/></p><p>A /dev/stdout naplókat nyomtat a normál kimenetre.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="849"/> + <source>Log file</source> + <translation>Naplófájl</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="863"/> + <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source> + <translation><html><head/><body><p>Az alapértelmezett időtartam akkor kerül végrehajtásra, ha nincs csatlakoztatva felhasználói felület.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="866"/> + <source>Default duration</source> + <translation>Alapértelmezett időtartam</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="879"/> + <source>Apply configuration to all nodes</source> + <translation>Beállítások alkalmazása az összes csomópontra</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="902"/> + <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source> + <translation><html><head/><body><p>Az alapértelmezett művelet akkor kerül végrehajtásra, ha nincs csatlakoztatva felhasználói felület.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="483"/> + <source>Default action</source> + <translation type="obsolete">Alapértelmezett művelet</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="921"/> + <source>HostName</source> + <translation>Állomásnév</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="671"/> + <source>proc</source> + <translation type="obsolete">proc</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="676"/> + <source>ebpf</source> + <translation type="obsolete">ebpf</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="681"/> + <source>audit</source> + <translation type="obsolete">audit</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="686"/> + <source>ftrace</source> + <translation type="obsolete">ftrace</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="975"/> + <source>until restart</source> + <translation>újraindításáig</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="980"/> + <source>always</source> + <translation>mindig</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="988"/> + <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source> + <translation><html><head/><body><p>A csomópont címe.</p><p>Alapértelmezett: unix:///tmp/osui.sock (Az „unix://” kötelező, ha Unix szoftvercsatorna)</p><p>Lehet IP-cím is a kikötővel: 127.0.0.1:50051</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="991"/> + <source>Address</source> + <translation>Cím</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="578"/> + <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons.</p><p>The pop-up dialog will only contain information about the network connection.</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Ha be van jelölve, az OpenSnitch több okból is meg fogja engedni vagy megtagadja azokat a kapcsolatokat, amelyek nem rendelkeznek társított folyamatazonosítóval.</p><p>A felbukkanó párbeszédpanel csak a hálózati kapcsolatról tartalmaz információkat.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="581"/> + <source>Intercept Unknown Connections</source> + <translation type="obsolete">Ismeretlen kapcsolatok elfogása</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1039"/> + <source>Version</source> + <translation>Változat</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="778"/> + <source>DEBUG</source> + <translation type="obsolete">HIBAKERESÉS</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="783"/> + <source>INFO</source> + <translation type="obsolete">TÁJÉKOTTATÁS</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="788"/> + <source>IMPORTANT</source> + <translation type="obsolete">FONTOS</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="793"/> + <source>WARNING</source> + <translation type="obsolete">FIGYELMEZTETÉS</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="798"/> + <source>ERROR</source> + <translation type="obsolete">HIBA</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="803"/> + <source>FATAL</source> + <translation type="obsolete">VÉGZETES</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1090"/> + <source>unix:///tmp/osui.sock</source> + <translation>unix:///tmp/osui.sock</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1102"/> + <source>/var/log/opensnitchd.log</source> + <translation>/var/log/opensnitchd.log</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1107"/> + <source>/dev/stdout</source> + <translation>/dev/stdout</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1131"/> + <source>Default log level</source> + <translation>Alapértelmezett naplószint</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1146"/> + <source>Database</source> + <translation>Adatbázis</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1200"/> + <source>Database type</source> + <translation>Adatbázistípus</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1207"/> + <source>Select</source> + <translation>Kijelölés</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1181"/> + <source>In memory</source> + <translation>Memóriabeli</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1186"/> + <source>File</source> + <translation>Fájl</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1482"/> + <source>Close</source> + <translation>Bezárás</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1493"/> + <source>Apply</source> + <translation>Alkalmazás</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1504"/> + <source>Save</source> + <translation>Mentés</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="356"/> + <source>The advanced view allows you to easily select multiple fields to filter connections</source> + <translation>A haladó nézet lehetővé teszi több mező egyszerű kiválasztását a kapcsolatok szűréséhez</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="110"/> + <source>If checked, this field will be selected when a pop-up is displayed</source> + <translation>Ha be van jelölve, akkor ez a mező lesz kiválasztva, amikor egy előugró jelenik meg</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="159"/> + <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source> + <translation><html><head/><body><p>Előugró ablak alapértelmezett művelete.</p><p>Amikor új kimenő kapcsolat jön létre, ez a művelet alapértelmezés szerint ki lesz választva, tehát ha az időtúllépés aktiválódik, akkor ez az opció lesz alkalmazva.</p><p><br/></p><p>Miközben egy előugró ablak kéri a felhasználót, hogy engedélyezze vagy tagadja meg a kapcsolatot:</p><p>1. megtagadják az új kimenő kapcsolatokat.</p><p>2. az ismert kapcsolatok a felhasználó által meghatározott szabályok alapján engedélyezhetők vagy megtagadhatók.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="905"/> + <source>Default action when the GUI is disconnected</source> + <translation>Alapértelmezett művelet a grafikus felhasználói felület leválasztása esetén</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1001"/> + <source>Debug invalid connections</source> + <translation>Érvénytelen kapcsolatok hibakeresése</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="39"/> + <source>Pop-ups</source> + <translation>Előugró ablakok</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="64"/> + <source>Default options</source> + <translation>Alapértelmezett beállítások</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="330"/> + <source>Default position on screen</source> + <translation>Alapértelmezett hely a képernyőn</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="769"/> + <source>any temporary rules</source> + <translation>bármilyen ideiglenes szabályt</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="487"/> + <source><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Ha ezt az opciót választja, a kiválasztott időtartam szabályai nem kerülnek hozzáadásra a grafikus felhasználói felület ideiglenes szabályainak listájához.</p><p><br/></p><p>Az ideiglenes szabályok továbbra is érvényesek, és használhatja őket, amikor a rendszer kéri az új kapcsolat engedélyezését/elutasítását.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="490"/> + <source>Don't save rules of duration</source> + <translation type="obsolete">Ne mentse az időtartam szabályait</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="589"/> + <source>Time</source> + <translation>Idő</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="669"/> + <source>Destination</source> + <translation>Cél</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="637"/> + <source>Protocol</source> + <translation>Protokoll</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="685"/> + <source>Process</source> + <translation>Folyamat</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="605"/> + <source>Rule</source> + <translation>Szabály</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="621"/> + <source>Node</source> + <translation>Csomópont</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="723"/> + <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using wireguard.</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Ha be van jelölve, az OpenSnitch felszólítja Önt, hogy engedélyezze vagy utasítsa el azokat a kapcsolatokat, amelyek nem rendelkeznek aszocizált folyamatazonosítóval, több okból, főleg a rossz állapotú kapcsolatok miatt.</p><p>Az előugró párbeszédablak csak a hálózati kapcsolatra vonatkozó adatokat tartalmazza.</p><p>Vannak azonban olyan esetek, amikor ezek érvényes kapcsolatok, például amikor virtuális magánhálózatot hoznak létre a WireGuard használatával.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="577"/> + <source>Events tab columns</source> + <translation>Események lap oszlopai</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="308"/> + <source>by PID</source> + <translation>folyamatazonosító által</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="473"/> + <source>Disable pop-ups, only display a notification</source> + <translation>Előugró ablakok letiltása, csak értesítés megjelenítése</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="496"/> + <source>Desktop notifications</source> + <translation>Asztali értesítések</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="514"/> + <source>Use system notifications</source> + <translation>Rendszerértesítések használata</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="530"/> + <source>Use Qt notifications</source> + <translation>Qt-értesítések használata</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="559"/> + <source>Test</source> + <translation>Tesztelés</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="998"/> + <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source> + <translation><html><head/><body><p>Ha be van jelölve, az OpenSnitch felkéri, hogy engedélyezze vagy tiltsa le azokat a kapcsolatokat, amelyekhez nem tartozik folyamatazonosító, több okból is, főleg a rossz állapotú kapcsolatok miatt.</p><p>A felugró párbeszédpanel csak a hálózati kapcsolatra vonatkozó információkat tartalmaz.</p><p>Vannak olyan esetek, amikor ezek érvényes kapcsolatok, például virtuális magánhálózat WireGuard segítségével történő létesítésekor.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1294"/> + <source>minutes</source> + <translation>perc</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1326"/> + <source>Minutes between events purges</source> + <translation>Eseménytisztítások közötti percek száma</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1352"/> + <source>days</source> + <translation>nap</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1365"/> + <source>Maximum days of events to keep</source> + <translation>Események megtartására nyitva álló napok száma</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="147"/> + <source>reject</source> + <translation>elutasítás</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="716"/> + <source>System</source> + <translation>Rendszer</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="695"/> + <source>Command line</source> + <translation>Parancssor</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="708"/> + <source>Theme</source> + <translation>Téma</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="748"/> + <source>Rules</source> + <translation>Szabályok</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="756"/> + <source>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI. + +Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</source> + <translation>Ha ez a lehetőség be van jelölve, a kiválasztott időtartamra vonatkozó szabályok nem lesznek hozzáadva az ideiglenes szabályok listájához a grafikus felhasználói felületen. + +Az ideiglenes szabályok továbbra is érvényesek maradnak, és használhatja őket, amikor a rendszer kéri, hogy engedélyezze/megtagadja az új kapcsolatot.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="761"/> + <source>Don't save/Delete rules of duration</source> + <translation>Ne mentse/törölje az időtartamra vonatkozó szabályokat:</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="779"/> + <source>30s or less</source> + <translation>30 másodperc vagy kevesebb</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="784"/> + <source>5m or less</source> + <translation>5 perc vagy kevesebb</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="789"/> + <source>15m or less</source> + <translation>15 perc vagy kevesebb</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="794"/> + <source>30m or less</source> + <translation>30 perc vagy kevesebb</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="799"/> + <source>1h or less</source> + <translation>1 óra vagy kevesebb</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="724"/> + <source>Language</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>ProcessDetailsDialog</name> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="14"/> + <source>Process details</source> + <translation>Folyamat részletei</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="61"/> + <source>loading...</source> + <translation>betöltés…</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="81"/> + <source>CWD: loading...</source> + <translation>CWD: betöltés…</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="93"/> + <source>mem stats: loading...</source> + <translation>memória statisztika: betöltés…</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="121"/> + <source>Status</source> + <translation>Állapot</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="135"/> + <source>Open files</source> + <translation>Fájlok megnyitása</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="149"/> + <source>I/O Statistics</source> + <translation>I/O statisztika</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="163"/> + <source>Memory mapped files</source> + <translation>Memóriába ágyazott fájlok</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="177"/> + <source>Stack</source> + <translation>Verem</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="191"/> + <source>Environment variables</source> + <translation>Környezeti változók</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="210"/> + <source>Application pids</source> + <translation>Alkalmazás folyamatazonosítók</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="240"/> + <source>Start or stop monitoring this process</source> + <translation>Folyamatfigyelés indítsa/leállítása</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="256"/> + <source>Close</source> + <translation>Bezárás</translation> + </message> +</context> +<context> + <name>RulesDialog</name> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="20"/> + <source>Rule</source> + <translation>Szabály</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> + <source>Node</source> + <translation>Csomópont</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> + <source>Apply rule to all nodes</source> + <translation>Alkalmazzon szabályt minden csomópontra</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="610"/> + <source>To this IP / Network</source> + <translation>Erre az IP-címre vagy a hálózatra</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="138"/> + <source>/path/to/executable, .*/bin/executable[0-9\.]+$, ...</source> + <translation type="obsolete">/útvonal/a/futtathatóhoz, .*/bináris/végrehajtható[0-9\.]+$, …</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="56"/> + <source>Action</source> + <translation>Művelet</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="902"/> + <source>To this port</source> + <translation>Erre a kikötőre</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="980"/> + <source>To this list of domains</source> + <translation>Ehhez a tartománylistához</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="760"/> + <source>You can specify a single IP: +- 192.168.1.1 + +or a regular expression: +- 192\.168\.1\.[0-9]+ + +multiple IPs: +- ^(192\.168\.1\.1|172\.16\.0\.1)$ + +You can also specify a subnet: +- 192.168.1.0/24 + +Note: Commas or spaces are not allowed to separate IPs or networks.</source> + <translation>Megadhat egyetlen IP-címet: +- 192.168.1.1 + +vagy reguláris kifejezés: +- 192\.168\.1\.[0-9]+ + +több IP-cím: +- ^(192\.168\.1\.1|172\.16\.0\.1)$ + +Megadhat alhálózatot is: +- 192.168.1.0/24 + +Megjegyzés: Vesszőkkel vagy szóközökkel nem szabad elválasztani az IP-címeket vagy a hálózatokat.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="459"/> + <source>LAN</source> + <translation type="obsolete">Helyi hálózat</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="464"/> + <source>127.0.0.0/8</source> + <translation type="obsolete">127.0.0.0/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="469"/> + <source>192.168.0.0/24</source> + <translation type="obsolete">192.168.0.0/24</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="474"/> + <source>192.168.1.0/24</source> + <translation type="obsolete">192.168.1.0/24</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> + <source>192.168.2.0/24</source> + <translation type="obsolete">192.168.2.0/24</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="484"/> + <source>192.168.0.0/16</source> + <translation type="obsolete">192.168.0.0/16</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="489"/> + <source>169.254.0.0/16</source> + <translation type="obsolete">169.254.0.0/16</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="494"/> + <source>172.16.0.0/12</source> + <translation type="obsolete">172.16.0.0/12</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="499"/> + <source>10.0.0.0/8</source> + <translation type="obsolete">10.0.0.0/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="504"/> + <source>::1/128</source> + <translation type="obsolete">::1/128</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="509"/> + <source>fc00::/7</source> + <translation type="obsolete">fc00::/7</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="514"/> + <source>ff00::/8</source> + <translation type="obsolete">ff00::/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="519"/> + <source>fe80::/10</source> + <translation type="obsolete">fe80::/10</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="524"/> + <source>fd00::/8</source> + <translation type="obsolete">fd00::/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="686"/> + <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Több kikötő megadható reguláris kifejezések használatával:</p><p><br/></p><p>- 53, 80 vagy 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 vagy 5551, 5552, 5553, stb:</p><p>^(53|443|555[0-9])$</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="97"/> + <source>once</source> + <translation>egyszer</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="797"/> + <source>30s</source> + <translation type="obsolete">30 másodperc</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="802"/> + <source>5m</source> + <translation type="obsolete">5 perc</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="807"/> + <source>15m</source> + <translation type="obsolete">15 perc</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="812"/> + <source>30m</source> + <translation type="obsolete">30 perc</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="817"/> + <source>1h</source> + <translation type="obsolete">1 óra</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="127"/> + <source>until reboot</source> + <translation>újraindításig</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="132"/> + <source>always</source> + <translation>mindig</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/> + <source>Commas or spaces are not allowed to specify multiple domains. + +Use regular expressions instead: +.*(opensnitch|duckduckgo).com +.*\.google.com + +or a single domain: +www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... +gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source> + <translation>Vesszők vagy szóközök nem engedélyezhetnek több tartomány megadását. + +Használjon helyette reguláris kifejezéseket: +.*(opensnitch|duckduckgo).com +.*\.google.com + +vagy egyetlen tartomány: +www.gnu.org - csak a www.gnu.org, nem az ftp.gnu.org, a www2.gnu.org, … +gnu.org - csak a gnu.org-nak fog megfelelni, nem a www.gnu.org-nak, nem az ftp.gnu.org-nak, …</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="603"/> + <source>www.domain.org, .*\.domain.org</source> + <translation>www.tartomány.hu, .*\.tartomány.hu</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="750"/> + <source>To this host</source> + <translation>Erre az állomásra</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="89"/> + <source>Duration</source> + <translation>Időtartam</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="526"/> + <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source> + <translation><html><head/><body><p>Csak TCP, UDP vagy UDPLITE engedélyezett</p><p>Használhatja a szabályos kifejezést, azaz: ^(TCP|UDP)$</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="532"/> + <source>TCP</source> + <translation>TCP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="338"/> + <source>UDP</source> + <translation type="obsolete">UDP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="343"/> + <source>UDPLITE</source> + <translation type="obsolete">UDPLITE</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="348"/> + <source>TCP6</source> + <translation type="obsolete">TCP6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="353"/> + <source>UDP6</source> + <translation type="obsolete">UDP6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="358"/> + <source>UDPLITE6</source> + <translation type="obsolete">UDPLITE6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="633"/> + <source>Protocol</source> + <translation>Protokoll</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="472"/> + <source>From this executable</source> + <translation>Ebből a futtatható fájlból</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="151"/> + <source>Deny</source> + <translation>Megtagadás</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="191"/> + <source>Allow</source> + <translation>Engedélyezés</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="379"/> + <source>From this command line</source> + <translation>Ebből a parancssorból</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="372"/> + <source>From this user ID</source> + <translation>Ebből a felhasználói azonosítóból</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="539"/> + <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Válasszon egy címtárat a letiltáshoz vagy engedélyezéshez szükséges tartománylistákkal.</p><p>Helyezze be a címtár fájlokat bármilyen kiterjesztéssel, amely tartalmazza a tartományok listáit.</p><p><br/>A lista minden bejegyzésének formátuma a következő (állomás formátum): </p><p>127.0.0.1 www.tartomány.hu</p><p>vagy </p><p>0.0.0.0 www.tartomány.hu</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="245"/> + <source>Name</source> + <translation type="unfinished">Név</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="207"/> + <source>Enable</source> + <translation>Engedélyezés</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="238"/> + <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them. + +000-allow-localhost +001-deny-broadcast +...</source> + <translation>A szabályokat ábécé sorrendben ellenőrzik, így azok prioritása szerint ennek megfelelően nevezheti meg őket. + +000-helyi-állomás-engedélyezése +001-közvetítés-tagadása +…</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="773"/> + <source>leave blank to autocreate</source> + <translation type="obsolete">hagyja üresen az önműködő létrehozáshoz</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="214"/> + <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one. + +You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example: + +[x] Priority - 000-priority-rule +[ ] Priority - 001-less-priority-rule</source> + <translation>Ha be van jelölve, akkor ez a szabály elsőbbséget élvez a többi szabálygal szemben. Ez után más szabályokat nem fogunk ellenőrizni. + +A szabályt úgy kell megneveznie, hogy először ellenőrizni fogják, mert betűrendben ellenőrzik. Például: + +[x] Elsőbbség - 000-elsőbbségi-szabály +[ ] Elsőbbség - 001-kevésbé-elsőbbségi-szabály</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="222"/> + <source>Priority rule</source> + <translation>Elsőbbségi szabály</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1092"/> + <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source> + <translation><html><head/><body><p>Alapértelmezés szerint a szabályok mezője nem különbözteti meg a kis- és nagybetűket, azaz ha egy folyamat megpróbálja elérni a gOOgle.CoM tartományt, és van egy szabályod, amelyet meg kell tagadni a .*google.com tartomány, a kapcsolat letiltva lesz.<br/></p><p>Ha bejelöli ezt a jelölőnégyzetet, meg kell adnia azt a pontos karakterláncot (tartomány, futtatható fájl, parancssor), amelyet szűrni szeretne.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1095"/> + <source>Case-sensitive</source> + <translation>Kis- és nagybetűk felismerése</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="346"/> + <source>Applications</source> + <translation>Alkalmazások</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="389"/> + <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source> + <translation><html><head/><body><p>Ez a mező tartalmazza a felhasználó által végrehajtott parancssort, és megegyezik vele.<br/></p><p>Ha a felhasználó beírta a parancsot, csak a parancsot megjelenik:</p><p>telnet 1.2.3.4<br/></p><p>Ha a felhasználó beírta a parancs abszolút vagy relatív elérési útját, akkor ez fog megjelenni:</p><p> >/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="399"/> + <source>From this PID</source> + <translation>Ebből a folyamatazonosítóból</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="491"/> + <source>Network</source> + <translation>Hálózat</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="932"/> + <source>List of domains/IPs</source> + <translation>Tartományok/IP-címek listája</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="938"/> + <source>To this list of network ranges</source> + <translation>Ehhez a hálózati tartományok listájához</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="945"/> + <source>To this list of IPs</source> + <translation>Ehhez az IP-címlistához</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="971"/> + <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation><html><head/><body><p>Válasszon ki egy könyvtárat a tiltandó vagy engedélyezendő IP-címek listáját tartalmazó fájlokkal:</p><p>1.2.3.4.5</p><p>1.2.3.4. 6</p><p>.</p><p>stb.</p><p>Soronként egy IP-cím. Az üres sorokat vagy a # karakterrel kezdődő sorokat figyelmen kívül hagyja.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1006"/> + <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation><html><head/><body><p>Válasszon ki egy könyvtárat a tiltandó vagy engedélyezni kívánt hálózati tartományok listáját tartalmazó fájlokkal:</p><p>1.2.3.0/24</p><p>80.34.56.0 /20</p><p>.</p><p>stb.<br/></p><p>Egy hálózati tartomány soronként. Az üres sorokat vagy a # karakterrel kezdődő sorokat figyelmen kívül hagyja.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1034"/> + <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source> + <translation><html><head/><body><p>Válasszon ki egy könyvtárat a tiltandó vagy engedélyezni kívánt tartományok listájával.</p><p>A könyvtárba helyezzen be olyan fájlokat, amelyek a tartomány listáit tartalmazzák.</p><p><br/>A lista minden egyes bejegyzésének formátuma a következő (gazdaformátum):</p><p>127.0.0.1 www.tartomány.com</p><p>vagy </p><p>0.0.0.0 www.tartomány.com</p><p>Az üres sorokat vagy a # karakterrel kezdődő sorokat figyelmen kívül hagyja.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1049"/> + <source>To this list of domains +(regular expressions)</source> + <translation>Ehhez a tartománylistához +(szabályos kifejezések)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1076"/> + <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation><html><head/><body><p>Válasszon ki egy könyvtárat a tiltandó vagy engedélyezendő tartományok szabványos kifejezéseit tartalmazó fájlokkal:</p><p>.*\.example\.com</p><p> Használhat olyan tartományt is, amilyen: &quot;example.com&quot;, és egyezik a bármi.példa.com, bármi.példa.com.helyitartomány stb. oldallal.</p><p>Soronként egy tartomány. Az üres sorokat vagy a # karakterrel kezdődő sorokat figyelmen kívül hagyja.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="168"/> + <source>Reject</source> + <translation>Elutasítás</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1151"/> + <source>Description...</source> + <translation>Leírás…</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="355"/> + <source><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></source> + <translation><html><head/><body><p>E mező értéke mindig a végrehajtható fájl abszolút elérési útja: /útvonal/a/binárishoz<br/></p><p>Példák:</p><p>- Egyszerű: /útvonal/a/binárishoz</p><p>- Több elérési út: ^/usr/lib(64|)/firefox/firefox$</p><p>- Több bináris fájl: ^( /usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- A /tmp fájl végrehajtásának megtagadása/engedélyezése:</p><p>^/(var/|)tmp/.*$<br/></p><p>További példákért keresse fel a <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki oldalon</a>, vagy érdeklődjön a <a href="https://github.com/evilsocket/opensnitch/discussions">vitafórumokon</a>.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="365"/> + <source>Is regular expression</source> + <translation>Szabályos kifejezés</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> + <source>is regular expression</source> + <translation>szabályos kifejezés</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="863"/> + <source>Network interface</source> + <translation>Hálózati felület</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1086"/> + <source>More</source> + <translation>Több</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1102"/> + <source>Don't log connections that match this rule</source> + <translation>Ne naplózza azokat a kapcsolatokat, amelyek megfelelnek ennek a szabálynak</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1105"/> + <source>Don't log connections</source> + <translation>Ne naplózza a kapcsolatokat</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="148"/> + <source>Deny will just discard the connection</source> + <translation>Megtagadás csak elveti a kapcsolatot</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="165"/> + <source>Reject will drop the connection, and kill the socket that initiated it</source> + <translation>Elutasítás megszakítja a kapcsolatot, és leállítja azt a szoftvercsatornát, amelyik kezdeményezte</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="185"/> + <source>Allow will allow the connection</source> + <translation>Engedélyezés lehetővé teszi a kapcsolatot</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="83"/> + <source>Name (leave blank to autocreate)</source> + <translation type="obsolete">Név (üres hagyása az automatikus létrehozáshoz)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="566"/> + <source>ICMP</source> + <translation>ICMP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="571"/> + <source>ICMP6</source> + <translation>ICMP6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="576"/> + <source>SCTP</source> + <translation>SCTP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="581"/> + <source>SCTP6</source> + <translation>SCTP6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="937"/> + <source>Color</source> + <translation type="obsolete">Szín</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="743"/> + <source>From this IP / Network</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="872"/> + <source>From this port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="918"/> + <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>StatsDialog</name> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="34"/> + <source>OpenSnitch Network Statistics</source> + <translation>OpenSnitch hálózati statisztika</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="287"/> + <source>Save to CSV</source> + <translation type="obsolete">Mentés CSV formátumban…</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="297"/> + <source>Ctrl+S</source> + <translation type="obsolete">Ctrl+S</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="333"/> + <source>Create a new rule</source> + <translation>Új szabály létrehozása</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="376"/> + <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source> + <translation><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">állomásnév - 192.168.1.1</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="413"/> + <source>Status</source> + <translation>Állapot</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1793"/> + <source>-</source> + <translation>-</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="451"/> + <source>Start or Stop interception</source> + <translation>Adatelérés indítása/leállítása</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="496"/> + <source>Events</source> + <translation>Események</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="94"/> + <source>Filter</source> + <translation>Szűrő</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="107"/> + <source>Allow</source> + <translation>Engedélyezés</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="116"/> + <source>Deny</source> + <translation>Megtagadás</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="143"/> + <source>Ex.: firefox</source> + <translation>Például: Firefox</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="205"/> + <source>50</source> + <translation>50</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="210"/> + <source>100</source> + <translation>100</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="215"/> + <source>200</source> + <translation>200</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="220"/> + <source>300</source> + <translation>300</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="233"/> + <source>Delete all intercepted events</source> + <translation>Az összes elfogott esemény törlése</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="826"/> + <source>Nodes</source> + <translation>Csomópontok</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="554"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Addr column to view details of a node)</span></p></body></html></source> + <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(kattintson duplán a Cím oszlopra a csomópont részleteinek megtekintéséhez)</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1700"/> + <source>Rules</source> + <translation>Szabályok</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="995"/> + <source>enable</source> + <translation>engedélyezés</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1022"/> + <source>Edit rule</source> + <translation>Szabály szerkesztése</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1039"/> + <source>Delete rule</source> + <translation>Szabály törlése</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="699"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click on a row to view details of a rule)</span></p></body></html></source> + <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(kattintson duplán egy sorra a szabály részleteinek megtekintéséhez)</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="692"/> + <source>search rule name</source> + <translation type="obsolete">szabálynév keresése</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="782"/> + <source>Application rules</source> + <translation>Alkalmazási szabályok</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="936"/> + <source>Permanent</source> + <translation>Állandó</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="945"/> + <source>Temporary</source> + <translation>Ideiglenes</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1063"/> + <source>Hosts</source> + <translation>Gazdagépek</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1364"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click to view details of an item)</span></p></body></html></source> + <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(kattintson duplán az elem részleteinek megtekintéséhez)</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="926"/> + <source>Delete all intercepted hosts</source> + <translation type="obsolete">Az összes elfogott gazdagép törlése</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1153"/> + <source>Applications</source> + <translation>Alkalmazások</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1051"/> + <source>Delete all intercepted applications</source> + <translation type="obsolete">Az összes elfogott alkalmazás törlése</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1266"/> + <source>Addresses</source> + <translation>Címek</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1159"/> + <source>Delete all intercepted addresses</source> + <translation type="obsolete">Az összes elfogott cím törlése</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1356"/> + <source>Ports</source> + <translation>Kikötők</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1261"/> + <source>Delete all intercepted ports</source> + <translation type="obsolete">Az összes elfogott port törlése</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1440"/> + <source>Users</source> + <translation>Felhasználók</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1371"/> + <source>Delete all intercepted users</source> + <translation type="obsolete">Az összes elfogott felhasználó törlése</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1544"/> + <source>Connections</source> + <translation>Kapcsolatok</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1596"/> + <source>Dropped</source> + <translation>Elvetve</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1648"/> + <source>Uptime</source> + <translation>Hasznos üzemidő</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1767"/> + <source>Version</source> + <translation>Változat</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="912"/> + <source>Delete connections that matched this rule</source> + <translation type="obsolete">Kapcsolat törlése amelyek megfelelnek ennek a szabálynak</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="927"/> + <source>All applications</source> + <translation>Minden alkalmazás</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="125"/> + <source>Reject</source> + <translation>Elutasítás</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="180"/> + <source>0</source> + <translation>0</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="777"/> + <source>2</source> + <translation>2</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="954"/> + <source>System rules</source> + <translation>Rendszer szabályai</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="637"/> + <source>Delete this node</source> + <translation>Csomópont törlése</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="653"/> + <source>Show the preferences of this node</source> + <translation>Csomópont-beállítások megjelenítése</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="669"/> + <source>Start or stop interception of this node</source> + <translation>Csomópont adatelérés indítása/leállítása</translation> + </message> +</context> +<context> + <name>contextual_menu</name> + <message> + <location filename="../../../opensnitch/service.py" line="47"/> + <source>Statistics</source> + <translation>Statisztika</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="48"/> + <source>Enable</source> + <translation>Engedélyezés</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="49"/> + <source>Disable</source> + <translation>Letiltás</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="50"/> + <source>Help</source> + <translation>Súgó</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="51"/> + <source>Close</source> + <translation>Bezárás</translation> + </message> +</context> +<context> + <name>firewall</name> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="91"/> + <source>Configuration applied.</source> + <translation>Beállítás alkalmazva.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="404"/> + <source>Error: {0}</source> + <translation>Hiba: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="193"/> + <source>Applying changes...</source> + <translation>Módosítások alkalmazása…</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="230"/> + <source>Error getting INPUT chain policy</source> + <translation>Hiba történt a BEMENET láncszabályzat lekérésekor</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="237"/> + <source>Error getting OUTPUT chain policy</source> + <translation>Hiba történt a KIMENET láncszabályzat lekérésekor</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="290"/> + <source>In order to configure firewall rules from the GUI, we need to use 'nftables' instead of 'iptables'</source> + <translation>A tűzfalszabályok grafikus felhasználói felületről történő beállításához az „iptables” helyett az „nftables”-t kell használni</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="304"/> + <source>Enabling firewall...</source> + <translation>Tűzfal engedélyezése…</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="306"/> + <source>Disabling firewall...</source> + <translation>Tűzfal letiltása…</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="71"/> + <source>Dest Port</source> + <translation>Célkikötő</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="72"/> + <source>Source Port</source> + <translation>Forráskikötő</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="73"/> + <source>Dest IP</source> + <translation>Cél IP-cím</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="74"/> + <source>Source IP</source> + <translation>Forrás IP-cím</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="75"/> + <source>Input interface</source> + <translation>Bemeneti felület</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="76"/> + <source>Output interface</source> + <translation>Kimeneti felület</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="77"/> + <source>Set conntrack mark</source> + <translation>conntrack-jelölés beállítása</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="78"/> + <source>Match conntrack mark</source> + <translation>conntrack-jelölés egyezése</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="79"/> + <source>Match conntrack state(s)</source> + <translation>conntrack-egyezés állapot(ok)</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="80"/> + <source>Set mark on packet</source> + <translation>Jelölés beállítása a csomagon</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="81"/> + <source>Match packet information</source> + <translation>Csomagadatok egyeztetése</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="87"/> + <source>Bandwidth quotas</source> + <translation>Sávszélesség-kvóták</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="89"/> + <source>Rate limit connections</source> + <translation>Sebességkorlát csatlakozások</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="373"/> + <source>Your protobuf version is incompatible, you need to install protobuf 3.8.0 or superior +(pip3 install --ignore-installed protobuf==3.8.0)</source> + <translation>A protobuf verziója nem kompatibilis, telepítenie kell a protobuf 3.8.0 vagy újabb verzióját +(pip3 install --ignore-installed protobuf==3.8.0)</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="397"/> + <source>Rule deleted</source> + <translation>Szabály törölve</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="401"/> + <source>Rule added</source> + <translation>Szabály hozzáadva</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="420"/> + <source>You can use ',' or '-' to specify multiple ports/IPs or ranges/values:<br><br>ports: 22 or 22,443 or 50000-60000<br>IPs: 192.168.1.1 or 192.168.1.30-192.168.1.130<br>Values: echo-reply,echo-request<br>Values: new,established,related</source> + <translation>A ',' vagy '-' karakterekkel több kikötők/IP-címet vagy tartományt/értéket adhat meg:<br><br>kikötők: 22 vagy 22,443 vagy 50000-60000<br>IP-címek: 192.168.1.1 vagy 192.168 .1.30-192.168.1.130<br>Értékek: echo-reply,echo-request<br>Értékek: new,established,related</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="440"/> + <source>Deleting rule, wait</source> + <translation>Szabály törlése, kérjük, várjon</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="443"/> + <source>Error updating rule</source> + <translation>Hiba történt a szabály frissítésekor</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="483"/> + <source>Adding rule, wait</source> + <translation>Szabály hozzáadása, kérjük, várjon</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="492"/> + <source><select a statement></source> + <translation><válasszon kijelentést></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="787"/> + <source>Equal</source> + <translation>Egyenlő</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="788"/> + <source>Not equal</source> + <translation>Nem egyenlő</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="789"/> + <source>Greater or equal than</source> + <translation>Egyenlő vagy nagyobb, mint</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="790"/> + <source>Greater than</source> + <translation>Nagyobb, mint</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="791"/> + <source>Less or equal than</source> + <translation>Egyenlő vagy kisebb, mint</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="792"/> + <source>Less than</source> + <translation>Kisebb, mint</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1350"/> + <source>Firewall rule</source> + <translation>Tűzfalszabály</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="885"/> + <source>Simple</source> + <translation>Egyszerű</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="890"/> + <source>Advanced</source> + <translation>Haladó</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1046"/> + <source>This rule is not supported yet.</source> + <translation>Ez a szabály még nem támogatott.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1111"/> + <source>Exclude service</source> + <translation>Szolgáltatás kizárása</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1123"/> + <source>Allow inbound connections to the selected port.</source> + <translation>Bejövő kapcsolatok engedélyezése a kiválasztott kikötőhöz.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1125"/> + <source>Allow outbound connections to the selected port.</source> + <translation>Kimenő kapcsolatok engedélyezése a kiválasztott kikötőhöz.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1201"/> + <source>select a statement.</source> + <translation>válasszon kijelentést.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1217"/> + <source>value cannot be 0 or empty.</source> + <translation>az érték nem lehet 0 vagy üres.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1229"/> + <source>the value format is 1024/kbytes (or bytes, mbytes, gbytes)</source> + <translation>az érték formátuma 1024/kbájt (vagy bájt, mbájt, gbájt)</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1240"/> + <source>the value format is 1024/kbytes/second (or bytes, mbytes, gbytes)</source> + <translation>az érték formátuma 1024/kbájt/másodperc (vagy bájt, mbájt, gbájt)</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1243"/> + <source>rate-limit not valid, use: bytes, kbytes, mbytes or gbytes.</source> + <translation>a sebességkorlát nem érvényes, használja: bájt, kbájt, mbájt vagy gbájt.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1245"/> + <source>time-limit not valid, use: second, minute, hour or day</source> + <translation>időkorlát nem érvényes, használja: másodperc, perc, óra vagy nap</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1293"/> + <source>port not valid.</source> + <translation>kikötő nem érvényes.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="108"/> + <source> +Supported formats: + + - Simple: 23 + - Ranges: 80-1024 + - Multiple ports: 80,443,8080 +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="134"/> + <source> +Supported formats: + + - Simple: 1.2.3.4 + - IP ranges: 1.2.3.100-1.2.3.200 + - Network ranges: 1.2.3.4/24 +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="147"/> + <source>Match input interface. Regular expressions not allowed.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="154"/> + <source>Match output interface. Regular expressions not allowed.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="161"/> + <source>Set a conntrack mark on the connection, in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="171"/> + <source>Match a conntrack mark of the connection, in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="178"/> + <source>Match conntrack states. + +Supported formats: + - Simple: new + - Multiple states separated by commas: related,new +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="193"/> + <source> +Match packet's metainformation. + +Value must be in decimal format, except for the "l4proto" option. +For l4proto it can be a lower case string, for example: + tcp + udp + icmp, + etc + +If the value is decimal for protocol or lproto, it'll use it as the code of +that protocol. +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="213"/> + <source>Set a mark on the packet matching the specified conditions. The value is in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="221"/> + <source> +Match ICMP codes. + +Supported formats: + - Simple: echo-request + - Multiple separated by commas: echo-request,echo-reply +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="234"/> + <source> +Match ICMPv6 codes. + +Supported formats: + - Simple: echo-request + - Multiple separated by commas: echo-request,echo-reply +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="247"/> + <source>Print a message when this rule matches a packet.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="254"/> + <source> +Apply quotas on connections. + +For example when: + - "quota over 10/mbytes" -> apply the Action defined (DROP) + - "quota until 10/mbytes" -> apply the Action defined (ACCEPT) + +The value must be in the format: VALUE/UNITS, for example: + - 10mbytes, 1/gbytes, etc +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="286"/> + <source> +Apply limits on connections. + +For example when: + - "limit over 10/mbytes/minute" -> apply the Action defined (DROP, ACCEPT, etc) + (When there're more than 10MB per minute, apply an Action) + + - "limit until 10/mbytes/hour" -> apply the Action defined (ACCEPT) + +The value must be in the format: VALUE/UNITS/TIME, for example: + - 10/mbytes/minute, 1/gbytes/hour, etc +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="607"/> + <source>num</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="621"/> + <source>to</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>messages</name> + <message> + <location filename="../../../opensnitch/service.py" line="281"/> + <source>Info</source> + <translation>Adat</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="285"/> + <source>Error</source> + <translation>Hiba</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="289"/> + <source>Warning</source> + <translation>Figyelmeztetés</translation> + </message> +</context> +<context> + <name>notifications</name> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="654"/> + <source>System notifications are not available, you need to install python3-notify2.</source> + <translation>A rendszerértesítések nem érhetők el, telepítenie kell a python3-notify2-t.</translation> + </message> +</context> +<context> + <name>popups</name> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="50"/> + <source>until reboot</source> + <translation>újraindításig</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/> + <source>forever</source> + <translation>örökre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="115"/> + <source>Allow</source> + <translation>Engedélyezés</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="114"/> + <source>Deny</source> + <translation>Megtagadás</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="331"/> + <source>Outgoing connection</source> + <translation>Kimenő kapcsolat</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="336"/> + <source>Process launched from:</source> + <translation>Folyamat innen indult:</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="373"/> + <source>from this executable</source> + <translation>ettől a végrehajtható fájlból</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="377"/> + <source>from this command line</source> + <translation>erről a parancssorról</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="379"/> + <source>to port {0}</source> + <translation>kikötőig: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="442"/> + <source>to {0}</source> + <translation>eddig: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="382"/> + <source>from user {0}</source> + <translation>a(z) {0} felhasználótól</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="399"/> + <source>to {0}.*</source> + <translation>eddig: {0}.*</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="452"/> + <source>to *.{0}</source> + <translation>eddig: *.{0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="378"/> + <source>to *{0}</source> + <translation type="obsolete">eddig: *{0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="486"/> + <source><b>Remote</b> process %s running on <b>%s</b></source> + <translation>A(z) %s <b>távoli</b> folyamat fut a(z) <b>%s</b>-n</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="490"/> + <source>is connecting to <b>%s</b> on %s port %d</source> + <translation>csatlakozik <b>%s</b>-hoz a %s-kikötőn %d</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="502"/> + <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source> + <translation>megpróbálja megoldani a(z) <b>%s</b> problémát a(z) %s segítségével, %s-kikötő %d</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="386"/> + <source>from this PID</source> + <translation>ebből a folyamatazonosítóból</translation> + </message> + <message> + <location filename="../../../opensnitch/notifications.py" line="122"/> + <source>New outgoing connection</source> + <translation>Új kimenő kapcsolat</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="116"/> + <source>Reject</source> + <translation>Elutasítás</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="497"/> + <source>is connecting to <b>%s</b>, %s</source> + <translation>csatlakozik a következőhöz: <b>%s</b>, %s</translation> + </message> + <message> + <location filename="../../../opensnitch/notifications.py" line="42"/> + <source>Open</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>preferences</name> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="386"/> + <source>Exception saving config: {0}</source> + <translation>Beállítás mentése kivétele: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> + <source>Warning</source> + <translation>Figyelmeztetés</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> + <source>You must select a file for the database<br>or choose "In memory" type.</source> + <translation>Válasszon egy fájlt az adatbázishoz<br>vagy válassza a „Memóriabeli” típust.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="401"/> + <source>DB type changed</source> + <translation>DB típusa megváltozott</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="38"/> + <source>Restart the GUI in order effects to take effect</source> + <translation>Indítsa újra a grafikus felhasználói felületet, hogy a hatások életbe léphessenek</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="500"/> + <source>Applying configuration on {0} ...</source> + <translation>Beállítások alkalmazása: {0}…</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="292"/> + <source>Server address can not be empty</source> + <translation>Kiszolgáló címe nem lehet üres</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="321"/> + <source>Error loading {0} configuration</source> + <translation>Hiba történt a(z) {0}-beállítás betöltésekor</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="568"/> + <source>Configuration applied.</source> + <translation>Beállítás alkalmazva.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="570"/> + <source>Error applying configuration: {0}</source> + <translation>Hiba a beállítás alkalmazásakor: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="607"/> + <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source> + <translation>A súgó megjelenítéséhez vigye az egeret a szövegek fölé<br><br>Emlékezzen meglátogatni a wikit: <a href="{0}">{0}</a></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="466"/> + <source>System</source> + <translation>Rendszer</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="187"/> + <source>Themes not available. Install qt-material: pip3 install qt-material</source> + <translation>A témák nem állnak rendelkezésre. Telepítse a qt-material-t: pip3 install qt-material</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> + <source>UI theme changed</source> + <translation>A felhasználói felület témája megváltozott</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> + <source>Restart the GUI in order to apply the new theme</source> + <translation>Indítsa újra a grafikus felhasználói felületet az új téma alkalmazásához</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="508"/> + <source>Ok</source> + <translation>Rendben</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="472"/> + <source>Restart the GUI in order changes to take effect</source> + <translation type="obsolete">Indítsa újra a grafikus felhasználói felületet, hogy a változtatások életbe lépjenek</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="388"/> + <source>There're no nodes connected</source> + <translation>Nincsenek csomópontok összekapcsolva</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="520"/> + <source>Exception saving node config {0}: {1}</source> + <translation>Kivétel menti a(z) {0} csomópont beállítását: {1}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="164"/> + <source>System default</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="433"/> + <source>Language changed</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>proc_details</name> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="100"/> + <source><b>Error loading process information:</b> <br><br> + +</source> + <translation><b>Hiba a folyamatadatok betöltésekor:</b> <br><br> + +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="119"/> + <source><b>Error stopping monitoring process:</b><br><br></source> + <translation><b>Hiba a megfigyelési folyamat leállításakor:</b><br><br></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="159"/> + <source>loading...</source> + <translation>betöltés folyamatban…</translation> + </message> +</context> +<context> + <name>rules</name> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="228"/> + <source>There're no nodes connected.</source> + <translation>Nincsenek csomópontok csatlakoztatva.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="271"/> + <source>Rule applied.</source> + <translation>A szabály alkalmazva.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="273"/> + <source>Error applying rule: {0}</source> + <translation>Hiba a szabály alkalmazásakor: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="539"/> + <source><b>Error loading rule</b></source> + <translation><b>Hiba történt a szabály betöltésekor</b></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="641"/> + <source>protocol can not be empty, or uncheck it</source> + <translation>a protokoll nem lehet üres, és nem szüntetheti meg a kijelölést</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="655"/> + <source>Protocol regexp error</source> + <translation>Protokoll szabályos kifejezéshibája</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="659"/> + <source>process path can not be empty</source> + <translation>folyamat útvonal nem lehet üres</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="673"/> + <source>Process path regexp error</source> + <translation>Folyamat útvonala szabályos kifejezéshibája</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="677"/> + <source>command line can not be empty</source> + <translation>parancssor nem lehet üres</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="691"/> + <source>Command line regexp error</source> + <translation>Parancssor szabályos kifejezéshibája</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="731"/> + <source>Dest port can not be empty</source> + <translation>Célkikötő nem lehet üres</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="745"/> + <source>Dst port regexp error</source> + <translation>Célkikötő szabványos kifejezéshibája</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="749"/> + <source>Dest host can not be empty</source> + <translation>Célállomás nem lehet üres</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="763"/> + <source>Dst host regexp error</source> + <translation>Célállomás szabályos kifejezéshibája</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="805"/> + <source>Dest IP/Network can not be empty</source> + <translation>Cél IP-cím/hálózat nem lehet üres</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="831"/> + <source>Dst IP regexp error</source> + <translation>Cél IP-cím szabályos kifejezéshibája</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="843"/> + <source>User ID can not be empty</source> + <translation>Felhasználói azonosító nem lehet üres</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="857"/> + <source>User ID regexp error</source> + <translation>Felhasználói azonosító szabályos kifejezéshibája</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="931"/> + <source>Lists field cannot be empty</source> + <translation>Listamező nem lehet üres</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="933"/> + <source>Lists field must be a directory</source> + <translation>Listmezők könyvtárnak kell lennie</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="976"/> + <source><b>Rule not supported</b></source> + <translation><b>A szabály nem támogatott</b></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="245"/> + <source>There's already a rule with this name.</source> + <translation>Már van egy szabály ezzel a névvel.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="861"/> + <source>PID field can not be empty</source> + <translation>Folyamatazonosító mező nem lehet üres</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="875"/> + <source>PID field regexp error</source> + <translation>Folyamatazonosító mező szabványos kifejezéshibája</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="963"/> + <source>Select at least one field.</source> + <translation>Jelöljön ki legalább egy mezőt.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="695"/> + <source>Network interface can not be empty</source> + <translation>Hálózati felület nem lehet üres</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="709"/> + <source>Network interface regexp error</source> + <translation>Hálózati felület szabályos kifejezéshibája</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="713"/> + <source>Source port can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="727"/> + <source>Source port regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="767"/> + <source>Source IP/Network can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="793"/> + <source>Source IP regexp error</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>stats</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> + <source>Name</source> + <translation type="obsolete">Név</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Address</source> + <translation type="obsolete">Cím</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="176"/> + <source>Status</source> + <translation type="obsolete">Állapot</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="177"/> + <source>Hostname</source> + <translation type="obsolete">Állomásnév</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="183"/> + <source>Version</source> + <translation type="obsolete">Változat</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="834"/> + <source>Rules</source> + <translation type="unfinished">Szabályok</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Time</source> + <translation type="obsolete">Idő</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="875"/> + <source>Action</source> + <translation type="unfinished">Művelet</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> + <source>Duration</source> + <translation type="obsolete">Időtartam</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> + <source>Node</source> + <translation type="obsolete">Csomópont</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Enabled</source> + <translation type="obsolete">Engedélyezve</translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="18"/> + <source>Hits</source> + <translation>Találatok száma</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>Protocol</source> + <translation type="obsolete">Protokoll</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="313"/> + <source>Not running</source> + <translation>Nem fut</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="314"/> + <source>Disabled</source> + <translation>Letiltva</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="315"/> + <source>Running</source> + <translation>Futtatás</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="636"/> + <source>OpenSnitch Network Statistics {0}</source> + <translation>{0} OpenSnitch hálózati statisztika</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="638"/> + <source>OpenSnitch Network Statistics for {0}</source> + <translation>OpenSnitch hálózati statisztikák a következőhöz: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1301"/> + <source><b>Error:</b><br><br></source> + <comment>{0}</comment> + <translation><b>Hiba:</b><br><br></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1308"/> + <source>Warning:</source> + <translation>Figyelmeztetés:</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="940"/> + <source>Allow</source> + <translation>Engedélyezés</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="941"/> + <source>Deny</source> + <translation>Megtagadás</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="945"/> + <source>Always</source> + <translation>Mindig</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="946"/> + <source>Until reboot</source> + <translation>Újraindításig</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="954"/> + <source>Disable</source> + <translation>Letiltás</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="956"/> + <source>Enable</source> + <translation>Engedélyezés</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="959"/> + <source>Duplicate</source> + <translation>Másolás</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="960"/> + <source>Edit</source> + <translation>Szerkesztés</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="961"/> + <source>Delete</source> + <translation>Törlés</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1189"/> + <source> Your are about to delete this rule. </source> + <translation> A szabály törlésére készül. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> + <source> Are you sure?</source> + <translation> Biztos benne?</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1248"/> + <source>Rule not found by that name and node</source> + <translation>A szabály nem található ezen a néven és csomóponton</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> + <source> You are about to delete this rule. </source> + <translation> A szabály törlésére készül. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2581"/> + <source>Save as CSV</source> + <translation>Mentés CSV-fájlként</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> + <source>Rule</source> + <translation type="obsolete">Szabály</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> + <source>Name</source> + <comment>xxxxx</comment> + <translation type="obsolete">Név</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> + <source>Name</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Név</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Address</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Cím</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> + <source>Status</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Állapot</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> + <source>Hostname</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Állomásnév</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> + <source>Version</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Változat</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> + <source>Rules</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Szabályok</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Time</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Idő</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> + <source>Action</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Művelet</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> + <source>Duration</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Időtartam</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> + <source>Node</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Csomópont</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Enabled</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Engedélyezve</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> + <source>Hits</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Találatok száma</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>Protocol</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Protokoll</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> + <source>Rule</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Szabály</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="287"/> + <source>Name</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Név</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="288"/> + <source>Address</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Cím</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="289"/> + <source>Status</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Állapot</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="290"/> + <source>Hostname</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Állomásnév</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="423"/> + <source>Version</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Változat</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="420"/> + <source>Rules</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Szabályok</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Time</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Idő</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> + <source>Action</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Művelet</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> + <source>Duration</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Időtartam</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> + <source>Node</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Csomópont</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Enabled</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Engedélyezve</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="438"/> + <source>Hits</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Találatok</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> + <source>Protocol</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Protokoll</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Process</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Folyamat</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>Destination</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Cél</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> + <source>Rule</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Szabály</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> + <source>UserID</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Felhasználóazonosító</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> + <source>LastConnection</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>LegutóbbiCsatlakozás</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> + <source>Args</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="obsolete">Argumentumok</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> + <source>DstIP</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>CélIPcíme</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> + <source>DstHost</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Célállomásneve</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> + <source>DstPort</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Célkikötő</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="174"/> + <source>LastConnection</source> + <translation type="obsolete">LegutóbbiCsatlakozás</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="179"/> + <source>Uptime</source> + <translation type="obsolete">Hasznos üzemidő</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="181"/> + <source>Connections</source> + <translation type="obsolete">Kapcsolatok</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="182"/> + <source>Dropped</source> + <translation type="obsolete">Elvetve</translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="17"/> + <source>What</source> + <translation>Mi</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="931"/> + <source>Apply to</source> + <translation>Alkalmazás a következőre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="942"/> + <source>Reject</source> + <translation>Elutasítás</translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="19"/> + <source>Network name</source> + <translation>Hálózatnév</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="291"/> + <source>Uptime</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Hasznos üzemidő</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="421"/> + <source>Connections</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Kapcsolatok</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="422"/> + <source>Dropped</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Elvetve</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="437"/> + <source>What</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Mi</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> + <source>Precedence</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Sorrend</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="776"/> + <source>New node connected</source> + <translation>Új csomópont csatlakoztatva</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> + <source>Description</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Leírás</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> + <source>Cmdline</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Parancssor</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="406"/> + <source>Export rules</source> + <translation>Exportszabályok</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="407"/> + <source>Import rules</source> + <translation>Importszabályok</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="408"/> + <source>Export events to CSV</source> + <translation>Események exportálása CSV-fájlként</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> + <source>Quit</source> + <translation>Kilépés</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="932"/> + <source>Export</source> + <translation>Exportálás</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="964"/> + <source>To clipboard</source> + <translation>A vágólapra</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="965"/> + <source>To disk</source> + <translation>Lemezre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2523"/> + <source>Select a directory to export rules</source> + <translation>Exportszabályok könyvtár kiválasztása</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1191"/> + <source> Your are about to delete this entry. </source> + <translation> A bejegyzés törlésére készül. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1678"/> + <source> You are about to delete this node. </source> + <translation> A csomópont törlésre készül. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1687"/> + <source><b>Error deleting node</b><br><br></source> + <comment>{0}</comment> + <translation><b>Hiba történt a csomópont törlésekor</b><br><br></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2478"/> + <source>Error exporting rules</source> + <translation>Hiba történt a szabályok exportálásakor</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2552"/> + <source>Select a directory with rules to import (JSON files)</source> + <translation>Importszabályok (JSON-fájlok) könyvtár kiválasztása</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2566"/> + <source>Rules imported fine</source> + <translation>Szabályok sikeresen importálva</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="211"/> + <source>WARNING</source> + <translation type="unfinished">FIGYELMEZTETÉS</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="833"/> + <source>Details</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="835"/> + <source>New</source> + <translation type="unfinished"></translation> + </message> +</context> +</TS> diff --git a/ui/i18n/locales/ja_JP/opensnitch-ja_JP.ts b/ui/i18n/locales/ja_JP/opensnitch-ja_JP.ts new file mode 100644 index 0000000..8e4c748 --- /dev/null +++ b/ui/i18n/locales/ja_JP/opensnitch-ja_JP.ts @@ -0,0 +1,3057 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="ja_JP"> +<context> + <name>Dialog</name> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="34"/> + <source>opensnitch-qt</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="300"/> + <source>User ID</source> + <translation type="unfinished">ユーザーID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="334"/> + <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source> + <translation type="unfinished"><html><head/><body><p><span style=" font-weight:600;">実行元</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="647"/> + <source>TextLabel</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="437"/> + <source>Source IP</source> + <translation type="unfinished">送信元のIP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="458"/> + <source>Process ID</source> + <translation type="unfinished">プロセスID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="601"/> + <source>Destination IP</source> + <translation type="unfinished">宛先IP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="622"/> + <source>Dst Port</source> + <translation type="unfinished">宛先ポート</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="702"/> + <source>from this executable</source> + <translation type="unfinished">この実行ファイルを</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="707"/> + <source>from this command line</source> + <translation type="unfinished">このコマンドラインを</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="712"/> + <source>this destination port</source> + <translation type="unfinished">この宛先ポートに対して</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="717"/> + <source>this user</source> + <translation type="unfinished">このユーザーを</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="722"/> + <source>this destination ip</source> + <translation type="unfinished">この宛先IPに対して</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="751"/> + <source>once</source> + <translation type="unfinished">一度のみ</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="756"/> + <source>30s</source> + <translation type="unfinished">30秒間</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="761"/> + <source>5m</source> + <translation type="unfinished">5分間</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="766"/> + <source>15m</source> + <translation type="unfinished">15分間</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="771"/> + <source>30m</source> + <translation type="unfinished">30分間</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="776"/> + <source>1h</source> + <translation type="unfinished">1時間</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="781"/> + <source>until reboot</source> + <translation type="unfinished">再起動するまで</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="786"/> + <source>forever</source> + <translation type="unfinished">永久に</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="346"/> + <source>Deny</source> + <translation type="unfinished">拒否する</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="337"/> + <source>Allow</source> + <translation type="unfinished">許可する</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="865"/> + <source>+</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="727"/> + <source>from this PID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="809"/> + <source>action</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="14"/> + <source>Firewall</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="55"/> + <source><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="320"/> + <source>Inbound</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="313"/> + <source>Outbound</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="275"/> + <source>Profile</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="375"/> + <source>Allow inbound connections to a port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="378"/> + <source>Allow service (IN)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="397"/> + <source>Exclude outbound connections to a port from being intercepted</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="406"/> + <source>Allow service (OUT)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="426"/> + <source>New rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="431"/> + <source>Close</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="14"/> + <source>Firewall rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="26"/> + <source>Node</source> + <translation type="unfinished">ノード</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="38"/> + <source>Enable</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="50"/> + <source>Description</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="90"/> + <source>Simple</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="154"/> + <source>Add new condition</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="177"/> + <source>Remove selected condition</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="233"/> + <source>Direction</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="248"/> + <source>IN</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="257"/> + <source>OUT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="223"/> + <source>Action</source> + <translation type="unfinished">アクション</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="285"/> + <source>ACCEPT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="294"/> + <source>DROP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="303"/> + <source>REJECT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="312"/> + <source>RETURN</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="442"/> + <source>Clear</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="453"/> + <source>Delete</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="464"/> + <source>Save</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="475"/> + <source>Add</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="266"/> + <source>FORWARD</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="271"/> + <source>PREROUTING</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="276"/> + <source>POSTROUTING</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="321"/> + <source>QUEUE</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="330"/> + <source>DNAT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="335"/> + <source>SNAT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="340"/> + <source>REDIRECT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="359"/> + <source>depending on the Action (i.e.: target), the syntaxis of the parameters will vary. +Some examples: + +QUEUE -> num 0 (or 1, 2, ...) +REDIRECT, TPROXY, DNAT, SNAT, MASQUERADE: + to :22 + to 192.168.1.254:8080 + to 192.168.1.254 + to 1024-2048 (masquerade)</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>PreferencesDialog</name> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="14"/> + <source>Preferences</source> + <translation type="unfinished">設定</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="484"/> + <source>UI</source> + <translation type="unfinished">UI</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="466"/> + <source>Default timeout</source> + <translation type="unfinished">ダイアログの表示時間</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="340"/> + <source>Pop-up default duration</source> + <translation type="unfinished">ポップアップ時に選択される規定のルールの有効期間</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="866"/> + <source>Default duration</source> + <translation type="unfinished">既定のルール有効期間</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="162"/> + <source>Pop-up default action</source> + <translation type="obsolete">ポップアップ時に選択される規定のアクション</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="483"/> + <source>Default action</source> + <translation type="obsolete">既定のアクション</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="323"/> + <source>Default target</source> + <translation type="unfinished">既定のターゲット</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="179"/> + <source>center</source> + <translation type="unfinished">中央</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="184"/> + <source>top right</source> + <translation type="unfinished">右上部</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="189"/> + <source>bottom right</source> + <translation type="unfinished">右下部</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="194"/> + <source>top left</source> + <translation type="unfinished">左上部</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="199"/> + <source>bottom left</source> + <translation type="unfinished">左下部</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="167"/> + <source>Prompt dialog default position on screen</source> + <translation type="obsolete">既定のダイアログ表示位置</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="283"/> + <source>by executable</source> + <translation type="unfinished">実行ファイル</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="288"/> + <source>by command line</source> + <translation type="unfinished">コマンドライン</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="293"/> + <source>by destination port</source> + <translation type="unfinished">宛先ポート</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="298"/> + <source>by destination ip</source> + <translation type="unfinished">宛先IP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="303"/> + <source>by user id</source> + <translation type="unfinished">ユーザーID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="970"/> + <source>once</source> + <translation type="unfinished">一度のみ</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="219"/> + <source>30s</source> + <translation type="unfinished">30秒間</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="224"/> + <source>5m</source> + <translation type="unfinished">5分間</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="229"/> + <source>15m</source> + <translation type="unfinished">15分間</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="234"/> + <source>30m</source> + <translation type="unfinished">30分間</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="239"/> + <source>1h</source> + <translation type="unfinished">1時間</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="244"/> + <source>until reboot</source> + <translation type="unfinished">再起動するまで</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="249"/> + <source>forever</source> + <translation type="unfinished">永久に</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1012"/> + <source>deny</source> + <translation type="unfinished">拒否</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1021"/> + <source>allow</source> + <translation type="unfinished">許可</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="406"/> + <source>Disable pop-ups, only display an alert</source> + <translation type="obsolete">ポップアップを無効にして通知のみ表示</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="823"/> + <source>Nodes</source> + <translation type="unfinished">ノード</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="829"/> + <source>Process monitor method</source> + <translation type="unfinished">プロセス監視方式</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="863"/> + <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source> + <translation type="unfinished"><html><head/><body><p>規定のルール有効期間は、UIが接続されていないときに使用されます。</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="988"/> + <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source> + <translation type="unfinished"><html><head/><body><p>ノードのアドレス</p><p>標準: unix:///tmp/osui.sock (Unixソケットの場合はunix://が必須)</p><p>このようにIPアドレスとポートを指定することもできます。127.0.0.1:50051</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="991"/> + <source>Address</source> + <translation type="unfinished">アドレス</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1131"/> + <source>Default log level</source> + <translation type="unfinished">既定のログレベル</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1039"/> + <source>Version</source> + <translation type="unfinished">バージョン</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="902"/> + <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source> + <translation type="unfinished"><html><head/><body><p>規定のアクションは、UIが接続されていないときに使用されます。</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="671"/> + <source>proc</source> + <translation type="obsolete">proc</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="681"/> + <source>audit</source> + <translation type="obsolete">audit</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="686"/> + <source>ftrace</source> + <translation type="obsolete">ftrace</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="846"/> + <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source> + <translation type="unfinished"><html><head/><body><p>ファイルにログを記録します<br/></p><p>/dev/stdoutにすると標準出力にログを出力します</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="849"/> + <source>Log file</source> + <translation type="unfinished">ログファイル</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="778"/> + <source>DEBUG</source> + <translation type="obsolete">DEBUG</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="783"/> + <source>INFO</source> + <translation type="obsolete">INFO</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="788"/> + <source>IMPORTANT</source> + <translation type="obsolete">IMPORTANT</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="793"/> + <source>WARNING</source> + <translation type="obsolete">WARNING</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="798"/> + <source>ERROR</source> + <translation type="obsolete">ERROR</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="803"/> + <source>FATAL</source> + <translation type="obsolete">FATAL</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="578"/> + <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons.</p><p>The pop-up dialog will only contain information about the network connection.</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>有効にした場合、opensnitchは、関連したPIDを持たない接続を許可するか拒否するかを確認します。</p><p>ポップアップダイアログには、ネットワーク接続に関する情報のみが表示されます。</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="581"/> + <source>Intercept Unknown Connections</source> + <translation type="obsolete">不明なプロセスを検証</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="921"/> + <source>HostName</source> + <translation type="unfinished">ホスト名</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1090"/> + <source>unix:///tmp/osui.sock</source> + <translation type="unfinished">unix:///tmp/osui.sock</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="975"/> + <source>until restart</source> + <translation type="unfinished">再起動するまで</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="980"/> + <source>always</source> + <translation type="unfinished">常に</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1102"/> + <source>/var/log/opensnitchd.log</source> + <translation type="unfinished">/var/log/opensnitchd.log</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1107"/> + <source>/dev/stdout</source> + <translation type="unfinished">/dev/stdout</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="879"/> + <source>Apply configuration to all nodes</source> + <translation type="unfinished">全てのノードに設定を反映</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1146"/> + <source>Database</source> + <translation type="unfinished">データベース</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1200"/> + <source>Database type</source> + <translation type="unfinished">データベース方式</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1207"/> + <source>Select</source> + <translation type="unfinished">参照</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1181"/> + <source>In memory</source> + <translation type="unfinished">メモリ内</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1186"/> + <source>File</source> + <translation type="unfinished">ファイル</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1482"/> + <source>Close</source> + <translation type="unfinished">閉じる</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1493"/> + <source>Apply</source> + <translation type="unfinished">適用</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1504"/> + <source>Save</source> + <translation type="unfinished">保存</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="83"/> + <source>Pop-ups default options</source> + <translation type="obsolete">ポップアップの規定のアクション</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="367"/> + <source>Pop-ups default position on screen</source> + <translation type="obsolete">規定のポップアップ表示位置</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="356"/> + <source>The advanced view allows you to easily select multiple fields to filter connections</source> + <translation type="unfinished">詳細表示では、接続をフィルタリングするために複数のフィールドを簡単に選択できます</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="359"/> + <source>Show advanced view by default</source> + <translation type="unfinished">標準で詳細表示を有効にする</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="653"/> + <source>Action</source> + <translation type="unfinished">アクション</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="375"/> + <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source> + <translation type="unfinished"><html><head/><body><p>有効にすると、詳細表示がアクティブな状態でポップアップが表示されます。</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="343"/> + <source>Duration</source> + <translation type="unfinished">ルールの有効期間</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="263"/> + <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source> + <translation type="unfinished"><html><head/><body><p>ポップアップの表示時、標準では接続の1つのプロパティ (実行可能ファイル、ポート、IP など) によって接続またはアプリケーションをフィルタリングできます。</p><p>これらのオプションを使用すると、接続をフィルタリングする際、複数のフィールドを選択できます。</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="266"/> + <source>Filter connections also by:</source> + <translation>次を使用して通信をフィルタ</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="110"/> + <source>If checked, this field will be selected when a pop-up is displayed</source> + <translation type="unfinished">有効にすると、ポップアップが表示されたときに、このフィールドが選択されます</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="81"/> + <source>User ID</source> + <translation type="unfinished">ユーザーID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="97"/> + <source>Destination port</source> + <translation type="unfinished">宛先ポート</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="113"/> + <source>Destination IP</source> + <translation type="unfinished">宛先IP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="463"/> + <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source> + <translation type="unfinished"><html><head/><body><p>このタイムアウトは、ポップアップダイアログの表示時間のカウントダウンです。</p><p>ポップアップに回答しない場合、このオプションが適用されます。</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="676"/> + <source>ebpf</source> + <translation type="obsolete">ebpf</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="159"/> + <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source> + <translation type="unfinished"><html><head/><body><p>ポップアップの規定のアクション</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="905"/> + <source>Default action when the GUI is disconnected</source> + <translation type="unfinished">GUI未接続時の規定のアクション</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1001"/> + <source>Debug invalid connections</source> + <translation type="unfinished">無効な接続をデバッグ</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="39"/> + <source>Pop-ups</source> + <translation type="unfinished">ポップアップ</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="64"/> + <source>Default options</source> + <translation type="unfinished">規定のオプション</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="330"/> + <source>Default position on screen</source> + <translation type="unfinished">規定の表示位置</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="769"/> + <source>any temporary rules</source> + <translation type="unfinished">全ての一時ルール</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="490"/> + <source>Don't save rules of duration</source> + <translation type="obsolete">ルールの有効期間を保持しない</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="589"/> + <source>Time</source> + <translation type="unfinished">時間</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="669"/> + <source>Destination</source> + <translation type="unfinished">宛先</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="637"/> + <source>Protocol</source> + <translation type="unfinished">プロトコル</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="685"/> + <source>Process</source> + <translation type="unfinished">プロセス</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="605"/> + <source>Rule</source> + <translation type="unfinished">ルール</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="621"/> + <source>Node</source> + <translation type="unfinished">ノード</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="577"/> + <source>Events tab columns</source> + <translation type="unfinished">イベントタブの項目</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="998"/> + <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="308"/> + <source>by PID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="473"/> + <source>Disable pop-ups, only display a notification</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="496"/> + <source>Desktop notifications</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="514"/> + <source>Use system notifications</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="530"/> + <source>Use Qt notifications</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="559"/> + <source>Test</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1294"/> + <source>minutes</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1326"/> + <source>Minutes between events purges</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1352"/> + <source>days</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1365"/> + <source>Maximum days of events to keep</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="147"/> + <source>reject</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="716"/> + <source>System</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="695"/> + <source>Command line</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="708"/> + <source>Theme</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="748"/> + <source>Rules</source> + <translation type="unfinished">ルール</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="756"/> + <source>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI. + +Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="761"/> + <source>Don't save/Delete rules of duration</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="779"/> + <source>30s or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="784"/> + <source>5m or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="789"/> + <source>15m or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="794"/> + <source>30m or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="799"/> + <source>1h or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="724"/> + <source>Language</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>ProcessDetailsDialog</name> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="14"/> + <source>Process details</source> + <translation type="unfinished">プロセス情報</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="61"/> + <source>loading...</source> + <translation type="unfinished">読み込み中...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="81"/> + <source>CWD: loading...</source> + <translation type="unfinished">CWD:-読み込み中...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="93"/> + <source>mem stats: loading...</source> + <translation type="unfinished">メモリ状態: 読み込み中...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="121"/> + <source>Status</source> + <translation type="unfinished">状態</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="135"/> + <source>Open files</source> + <translation type="unfinished">ファイルアクセス</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="149"/> + <source>I/O Statistics</source> + <translation type="unfinished">入出力の統計</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="163"/> + <source>Memory mapped files</source> + <translation type="unfinished">メモリ内データ</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="177"/> + <source>Stack</source> + <translation type="unfinished">スタック</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="191"/> + <source>Environment variables</source> + <translation type="unfinished">環境変数</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="210"/> + <source>Application pids</source> + <translation type="unfinished">プロセスID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="240"/> + <source>Start or stop monitoring this process</source> + <translation type="unfinished">プロセスの監視を開始/停止</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="256"/> + <source>Close</source> + <translation type="unfinished">閉じる</translation> + </message> +</context> +<context> + <name>RulesDialog</name> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="20"/> + <source>Rule</source> + <translation type="unfinished">ルール</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> + <source>Node</source> + <translation type="unfinished">ノード</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> + <source>Apply rule to all nodes</source> + <translation type="unfinished">全てのノードにルールを反映</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="610"/> + <source>To this IP / Network</source> + <translation type="unfinished">IP/ネットワーク</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="56"/> + <source>Action</source> + <translation type="unfinished">アクション</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="902"/> + <source>To this port</source> + <translation type="unfinished">ポート</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="980"/> + <source>To this list of domains</source> + <translation type="unfinished">ドメインリスト</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="760"/> + <source>You can specify a single IP: +- 192.168.1.1 + +or a regular expression: +- 192\.168\.1\.[0-9]+ + +multiple IPs: +- ^(192\.168\.1\.1|172\.16\.0\.1)$ + +You can also specify a subnet: +- 192.168.1.0/24 + +Note: Commas or spaces are not allowed to separate IPs or networks.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="459"/> + <source>LAN</source> + <translation type="obsolete">LAN</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="97"/> + <source>once</source> + <translation type="unfinished">一度のみ</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="797"/> + <source>30s</source> + <translation type="obsolete">30秒間</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="802"/> + <source>5m</source> + <translation type="obsolete">5分間</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="807"/> + <source>15m</source> + <translation type="obsolete">15分間</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="812"/> + <source>30m</source> + <translation type="obsolete">30分間</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="817"/> + <source>1h</source> + <translation type="obsolete">1時間</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="127"/> + <source>until reboot</source> + <translation type="unfinished">再起動するまで</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="132"/> + <source>always</source> + <translation type="unfinished">常に</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/> + <source>Commas or spaces are not allowed to specify multiple domains. + +Use regular expressions instead: +.*(opensnitch|duckduckgo).com +.*\.google.com + +or a single domain: +www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... +gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="603"/> + <source>www.domain.org, .*\.domain.org</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="750"/> + <source>To this host</source> + <translation type="unfinished">ホスト</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="89"/> + <source>Duration</source> + <translation type="unfinished">有効期間</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="526"/> + <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="532"/> + <source>TCP</source> + <translation type="unfinished">TCP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="338"/> + <source>UDP</source> + <translation type="obsolete">UDP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="343"/> + <source>UDPLITE</source> + <translation type="obsolete">UDPLITE</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="348"/> + <source>TCP6</source> + <translation type="obsolete">TCP6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="353"/> + <source>UDP6</source> + <translation type="obsolete">UDP6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="358"/> + <source>UDPLITE6</source> + <translation type="obsolete">UDPLITE6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="633"/> + <source>Protocol</source> + <translation type="unfinished">プロトコル</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="472"/> + <source>From this executable</source> + <translation type="unfinished">実行ファイル</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="151"/> + <source>Deny</source> + <translation type="unfinished">拒否</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="191"/> + <source>Allow</source> + <translation type="unfinished">許可</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="379"/> + <source>From this command line</source> + <translation type="unfinished">コマンドライン</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="372"/> + <source>From this user ID</source> + <translation type="unfinished">ユーザーID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="245"/> + <source>Name</source> + <translation type="unfinished">名前</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="207"/> + <source>Enable</source> + <translation type="unfinished">有効</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="238"/> + <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them. + +000-allow-localhost +001-deny-broadcast +...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="773"/> + <source>leave blank to autocreate</source> + <translation type="obsolete">空にすると自動生成されます</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="214"/> + <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one. + +You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example: + +[x] Priority - 000-priority-rule +[ ] Priority - 001-less-priority-rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="222"/> + <source>Priority rule</source> + <translation type="unfinished">優先ルール</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1092"/> + <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1095"/> + <source>Case-sensitive</source> + <translation type="unfinished">大文字/小文字を区別</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="346"/> + <source>Applications</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="389"/> + <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="399"/> + <source>From this PID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="491"/> + <source>Network</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="932"/> + <source>List of domains/IPs</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="938"/> + <source>To this list of network ranges</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="945"/> + <source>To this list of IPs</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="971"/> + <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1006"/> + <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1034"/> + <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1049"/> + <source>To this list of domains +(regular expressions)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1076"/> + <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="168"/> + <source>Reject</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1151"/> + <source>Description...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="355"/> + <source><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="365"/> + <source>Is regular expression</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> + <source>is regular expression</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="863"/> + <source>Network interface</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1086"/> + <source>More</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1102"/> + <source>Don't log connections that match this rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1105"/> + <source>Don't log connections</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="148"/> + <source>Deny will just discard the connection</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="165"/> + <source>Reject will drop the connection, and kill the socket that initiated it</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="185"/> + <source>Allow will allow the connection</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="566"/> + <source>ICMP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="571"/> + <source>ICMP6</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="576"/> + <source>SCTP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="581"/> + <source>SCTP6</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="743"/> + <source>From this IP / Network</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="872"/> + <source>From this port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="918"/> + <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>StatsDialog</name> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="34"/> + <source>OpenSnitch Network Statistics</source> + <translation type="unfinished">OpenSnitchネットワークモニター</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="287"/> + <source>Save to CSV</source> + <translation type="obsolete">CSVファイルに保存</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="297"/> + <source>Ctrl+S</source> + <translation type="obsolete">Ctrl+S</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="333"/> + <source>Create a new rule</source> + <translation type="unfinished">ルールを新規作成</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="376"/> + <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source> + <translation type="unfinished"><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">ホスト名 - 192.168.1.1</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="413"/> + <source>Status</source> + <translation type="unfinished">状態</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1793"/> + <source>-</source> + <translation type="unfinished">-</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="451"/> + <source>Start or Stop interception</source> + <translation type="unfinished">サービスを開始/停止</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="496"/> + <source>Events</source> + <translation type="unfinished">イベント</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="94"/> + <source>Filter</source> + <translation type="unfinished">絞り込み</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="107"/> + <source>Allow</source> + <translation type="unfinished">許可中</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="116"/> + <source>Deny</source> + <translation type="unfinished">拒否中</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="143"/> + <source>Ex.: firefox</source> + <translation type="unfinished">例:firefox</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="205"/> + <source>50</source> + <translation type="unfinished">50</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="210"/> + <source>100</source> + <translation type="unfinished">100</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="215"/> + <source>200</source> + <translation type="unfinished">200</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="220"/> + <source>300</source> + <translation type="unfinished">300</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="233"/> + <source>Delete all intercepted events</source> + <translation type="unfinished">記録した全てのイベント履歴を消去</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="826"/> + <source>Nodes</source> + <translation type="unfinished">ノード</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="554"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Addr column to view details of a node)</span></p></body></html></source> + <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(項目をダブルクリックでノードの詳細を確認できます)</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1700"/> + <source>Rules</source> + <translation type="unfinished">ルール</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="995"/> + <source>enable</source> + <translation type="unfinished">有効</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1022"/> + <source>Edit rule</source> + <translation type="unfinished">ルールを編集</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1039"/> + <source>Delete rule</source> + <translation type="unfinished">ルールを削除</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="674"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Name column to view details of a rule)</span></p></body></html></source> + <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(項目をダブルクリックでルールの詳細を確認できます)</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="692"/> + <source>search rule name</source> + <translation type="obsolete">ルール名を検索</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="782"/> + <source>Application rules</source> + <translation type="unfinished">アプリケーションのルール</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="936"/> + <source>Permanent</source> + <translation type="unfinished">永久ルール</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="945"/> + <source>Temporary</source> + <translation type="unfinished">一時ルール</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1063"/> + <source>Hosts</source> + <translation type="unfinished">ホスト</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1364"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click to view details of an item)</span></p></body></html></source> + <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(項目をダブルクリックで詳細を確認できます)</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="926"/> + <source>Delete all intercepted hosts</source> + <translation type="obsolete">記録した全てのホストを消去</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1153"/> + <source>Applications</source> + <translation type="unfinished">アプリケーション</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1051"/> + <source>Delete all intercepted applications</source> + <translation type="obsolete">記録した全てのアプリケーション履歴を消去</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1266"/> + <source>Addresses</source> + <translation type="unfinished">アドレス</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1159"/> + <source>Delete all intercepted addresses</source> + <translation type="obsolete">記録した全てのアドレスを消去</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1356"/> + <source>Ports</source> + <translation type="unfinished">ポート</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1261"/> + <source>Delete all intercepted ports</source> + <translation type="obsolete">記録した全てのポートを消去</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1440"/> + <source>Users</source> + <translation type="unfinished">ユーザー</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1371"/> + <source>Delete all intercepted users</source> + <translation type="obsolete">記録した全てのユーザー履歴を消去</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1544"/> + <source>Connections</source> + <translation type="unfinished">通過</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1596"/> + <source>Dropped</source> + <translation type="unfinished">ブロック</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1648"/> + <source>Uptime</source> + <translation type="unfinished">実行時間</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1767"/> + <source>Version</source> + <translation type="unfinished">バージョン</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="699"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click on a row to view details of a rule)</span></p></body></html></source> + <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(項目をダブルクリックするとルールの詳細が確認できます)</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="912"/> + <source>Delete connections that matched this rule</source> + <translation type="obsolete">このルールにマッチした接続を削除します</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="927"/> + <source>All applications</source> + <translation type="unfinished">全てのアプリケーション</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="125"/> + <source>Reject</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="180"/> + <source>0</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="777"/> + <source>2</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="954"/> + <source>System rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="637"/> + <source>Delete this node</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="653"/> + <source>Show the preferences of this node</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="669"/> + <source>Start or stop interception of this node</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>contextual_menu</name> + <message> + <location filename="../../../opensnitch/service.py" line="47"/> + <source>Statistics</source> + <translation type="unfinished">ダッシュボードを開く</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="50"/> + <source>Help</source> + <translation type="unfinished">ヘルプ</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="51"/> + <source>Close</source> + <translation type="unfinished">終了</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="48"/> + <source>Enable</source> + <translation type="unfinished">有効化</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="49"/> + <source>Disable</source> + <translation type="unfinished">無効化</translation> + </message> +</context> +<context> + <name>firewall</name> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="91"/> + <source>Configuration applied.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="404"/> + <source>Error: {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="193"/> + <source>Applying changes...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="230"/> + <source>Error getting INPUT chain policy</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="237"/> + <source>Error getting OUTPUT chain policy</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="290"/> + <source>In order to configure firewall rules from the GUI, we need to use 'nftables' instead of 'iptables'</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="304"/> + <source>Enabling firewall...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="306"/> + <source>Disabling firewall...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="71"/> + <source>Dest Port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="72"/> + <source>Source Port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="73"/> + <source>Dest IP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="74"/> + <source>Source IP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="75"/> + <source>Input interface</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="76"/> + <source>Output interface</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="77"/> + <source>Set conntrack mark</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="78"/> + <source>Match conntrack mark</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="79"/> + <source>Match conntrack state(s)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="80"/> + <source>Set mark on packet</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="81"/> + <source>Match packet information</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="87"/> + <source>Bandwidth quotas</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="89"/> + <source>Rate limit connections</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="373"/> + <source>Your protobuf version is incompatible, you need to install protobuf 3.8.0 or superior +(pip3 install --ignore-installed protobuf==3.8.0)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="397"/> + <source>Rule deleted</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="401"/> + <source>Rule added</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="420"/> + <source>You can use ',' or '-' to specify multiple ports/IPs or ranges/values:<br><br>ports: 22 or 22,443 or 50000-60000<br>IPs: 192.168.1.1 or 192.168.1.30-192.168.1.130<br>Values: echo-reply,echo-request<br>Values: new,established,related</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="440"/> + <source>Deleting rule, wait</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="443"/> + <source>Error updating rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="483"/> + <source>Adding rule, wait</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="492"/> + <source><select a statement></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="787"/> + <source>Equal</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="788"/> + <source>Not equal</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="789"/> + <source>Greater or equal than</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="790"/> + <source>Greater than</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="791"/> + <source>Less or equal than</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="792"/> + <source>Less than</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1350"/> + <source>Firewall rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="885"/> + <source>Simple</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="890"/> + <source>Advanced</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1046"/> + <source>This rule is not supported yet.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1111"/> + <source>Exclude service</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1123"/> + <source>Allow inbound connections to the selected port.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1125"/> + <source>Allow outbound connections to the selected port.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1201"/> + <source>select a statement.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1217"/> + <source>value cannot be 0 or empty.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1229"/> + <source>the value format is 1024/kbytes (or bytes, mbytes, gbytes)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1240"/> + <source>the value format is 1024/kbytes/second (or bytes, mbytes, gbytes)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1243"/> + <source>rate-limit not valid, use: bytes, kbytes, mbytes or gbytes.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1245"/> + <source>time-limit not valid, use: second, minute, hour or day</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1293"/> + <source>port not valid.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="108"/> + <source> +Supported formats: + + - Simple: 23 + - Ranges: 80-1024 + - Multiple ports: 80,443,8080 +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="134"/> + <source> +Supported formats: + + - Simple: 1.2.3.4 + - IP ranges: 1.2.3.100-1.2.3.200 + - Network ranges: 1.2.3.4/24 +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="147"/> + <source>Match input interface. Regular expressions not allowed.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="154"/> + <source>Match output interface. Regular expressions not allowed.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="161"/> + <source>Set a conntrack mark on the connection, in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="171"/> + <source>Match a conntrack mark of the connection, in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="178"/> + <source>Match conntrack states. + +Supported formats: + - Simple: new + - Multiple states separated by commas: related,new +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="193"/> + <source> +Match packet's metainformation. + +Value must be in decimal format, except for the "l4proto" option. +For l4proto it can be a lower case string, for example: + tcp + udp + icmp, + etc + +If the value is decimal for protocol or lproto, it'll use it as the code of +that protocol. +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="213"/> + <source>Set a mark on the packet matching the specified conditions. The value is in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="221"/> + <source> +Match ICMP codes. + +Supported formats: + - Simple: echo-request + - Multiple separated by commas: echo-request,echo-reply +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="234"/> + <source> +Match ICMPv6 codes. + +Supported formats: + - Simple: echo-request + - Multiple separated by commas: echo-request,echo-reply +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="247"/> + <source>Print a message when this rule matches a packet.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="254"/> + <source> +Apply quotas on connections. + +For example when: + - "quota over 10/mbytes" -> apply the Action defined (DROP) + - "quota until 10/mbytes" -> apply the Action defined (ACCEPT) + +The value must be in the format: VALUE/UNITS, for example: + - 10mbytes, 1/gbytes, etc +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="286"/> + <source> +Apply limits on connections. + +For example when: + - "limit over 10/mbytes/minute" -> apply the Action defined (DROP, ACCEPT, etc) + (When there're more than 10MB per minute, apply an Action) + + - "limit until 10/mbytes/hour" -> apply the Action defined (ACCEPT) + +The value must be in the format: VALUE/UNITS/TIME, for example: + - 10/mbytes/minute, 1/gbytes/hour, etc +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="607"/> + <source>num</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="621"/> + <source>to</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>messages</name> + <message> + <location filename="../../../opensnitch/service.py" line="281"/> + <source>Info</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="285"/> + <source>Error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="289"/> + <source>Warning</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>notifications</name> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="654"/> + <source>System notifications are not available, you need to install python3-notify2.</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>popups</name> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="50"/> + <source>until reboot</source> + <translation type="unfinished">再起動するまで</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/> + <source>forever</source> + <translation type="unfinished">永久に</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="115"/> + <source>Allow</source> + <translation type="unfinished">許可</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="114"/> + <source>Deny</source> + <translation type="unfinished">拒否</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="331"/> + <source>Outgoing connection</source> + <translation type="unfinished">外部への接続</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="336"/> + <source>Process launched from:</source> + <translation type="unfinished">プロセスの実行元:</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="373"/> + <source>from this executable</source> + <translation type="unfinished">次の実行ファイルを</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="377"/> + <source>from this command line</source> + <translation type="unfinished">次のコマンドラインを</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="379"/> + <source>to port {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="442"/> + <source>to {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="382"/> + <source>from user {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="399"/> + <source>to {0}.*</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="452"/> + <source>to *.{0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="486"/> + <source><b>Remote</b> process %s running on <b>%s</b></source> + <translation type="unfinished"><b>リモート</b>プロセス %s は <b>%s</b> で実行中です</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="490"/> + <source>is connecting to <b>%s</b> on %s port %d</source> + <translation type="unfinished">は <b>%s</b> の %s ポート %d 番に接続しています</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="502"/> + <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source> + <translation type="unfinished">は<b>%s</b> を %sの %s ポート %dで解決しようとしています</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="386"/> + <source>from this PID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/notifications.py" line="122"/> + <source>New outgoing connection</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="116"/> + <source>Reject</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="497"/> + <source>is connecting to <b>%s</b>, %s</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/notifications.py" line="42"/> + <source>Open</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>preferences</name> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="386"/> + <source>Exception saving config: {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> + <source>Warning</source> + <translation type="unfinished">警告</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> + <source>You must select a file for the database<br>or choose "In memory" type.</source> + <translation type="unfinished">データベースの保存ファイルを選択するか、方式「メモリ内」を選択する必要があります。</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="401"/> + <source>DB type changed</source> + <translation type="unfinished">データベース方式が変更されました</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="38"/> + <source>Restart the GUI in order effects to take effect</source> + <translation type="unfinished">GUIを再起動後反映されます</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="500"/> + <source>Applying configuration on {0} ...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="292"/> + <source>Server address can not be empty</source> + <translation type="unfinished">サーバーアドレスは空白にすることはできません</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="321"/> + <source>Error loading {0} configuration</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="568"/> + <source>Configuration applied.</source> + <translation type="unfinished">構成は反映されました。</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="570"/> + <source>Error applying configuration: {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="607"/> + <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="466"/> + <source>System</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="187"/> + <source>Themes not available. Install qt-material: pip3 install qt-material</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> + <source>UI theme changed</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> + <source>Restart the GUI in order to apply the new theme</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="508"/> + <source>Ok</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="388"/> + <source>There're no nodes connected</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="520"/> + <source>Exception saving node config {0}: {1}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="164"/> + <source>System default</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="433"/> + <source>Language changed</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>proc_details</name> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="100"/> + <source><b>Error loading process information:</b> <br><br> + +</source> + <translation type="unfinished"><b>プロセス情報の読み込みでエラーが発生しました:</b> <br><br> + +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="119"/> + <source><b>Error stopping monitoring process:</b><br><br></source> + <translation type="unfinished"><b>プロセス監視の停止中にエラーが発生しました:</b><br><br></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="159"/> + <source>loading...</source> + <translation type="unfinished">読み込み中...</translation> + </message> +</context> +<context> + <name>rules</name> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="228"/> + <source>There're no nodes connected.</source> + <translation type="unfinished">接続しているノードがありません。</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="271"/> + <source>Rule applied.</source> + <translation type="unfinished">ルールが反映されました。</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="273"/> + <source>Error applying rule: {0}</source> + <translation type="unfinished">ルールの反映に失敗しました: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="641"/> + <source>protocol can not be empty, or uncheck it</source> + <translation type="unfinished">プロトコルを指定するかチェックを外してください</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="655"/> + <source>Protocol regexp error</source> + <translation type="unfinished">プロトコルの正規表現記法が誤っています</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="659"/> + <source>process path can not be empty</source> + <translation type="unfinished">プロセスのパスを指定してください</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="673"/> + <source>Process path regexp error</source> + <translation type="unfinished">プロセスパスの正規表現記法が誤っています</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="677"/> + <source>command line can not be empty</source> + <translation type="unfinished">コマンドラインを指定してください</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="691"/> + <source>Command line regexp error</source> + <translation type="unfinished">コマンドラインの正規表現記法が誤っています</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="731"/> + <source>Dest port can not be empty</source> + <translation type="unfinished">宛先ポートを指定してください</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="745"/> + <source>Dst port regexp error</source> + <translation type="unfinished">宛先ポートの正規表現記法が誤っています</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="749"/> + <source>Dest host can not be empty</source> + <translation type="unfinished">宛先ホストを指定してください</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="763"/> + <source>Dst host regexp error</source> + <translation type="unfinished">宛先ホストの正規表現記法が誤っています</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="805"/> + <source>Dest IP/Network can not be empty</source> + <translation type="unfinished">宛先IP/ネットワークを指定してください</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="831"/> + <source>Dst IP regexp error</source> + <translation type="unfinished">宛先IPの正規表現記法が誤っています</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="843"/> + <source>User ID can not be empty</source> + <translation type="unfinished">ユーザーIDを指定してください</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="857"/> + <source>User ID regexp error</source> + <translation type="unfinished">ユーザーIDの正規表現記法が誤っています</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="931"/> + <source>Lists field cannot be empty</source> + <translation type="unfinished">リスト項目を指定してください</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="933"/> + <source>Lists field must be a directory</source> + <translation type="unfinished">リスト項目には必ずディレクトリを指定してください</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="976"/> + <source><b>Rule not supported</b></source> + <translation type="unfinished"><b>ルールをサポートしていません</b></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="539"/> + <source><b>Error loading rule</b></source> + <translation type="unfinished"><b>ルールの読み込みに失敗しました</b></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="245"/> + <source>There's already a rule with this name.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="861"/> + <source>PID field can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="875"/> + <source>PID field regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="963"/> + <source>Select at least one field.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="695"/> + <source>Network interface can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="709"/> + <source>Network interface regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="713"/> + <source>Source port can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="727"/> + <source>Source port regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="767"/> + <source>Source IP/Network can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="793"/> + <source>Source IP regexp error</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>stats</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> + <source>Name</source> + <translation type="obsolete">名前</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Address</source> + <translation type="obsolete">アドレス</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="176"/> + <source>Status</source> + <translation type="obsolete">状態</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="177"/> + <source>Hostname</source> + <translation type="obsolete">ホスト名</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="183"/> + <source>Version</source> + <translation type="obsolete">バージョン</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="834"/> + <source>Rules</source> + <translation type="unfinished">ルール</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Time</source> + <translation type="obsolete">時間</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="875"/> + <source>Action</source> + <translation type="unfinished">アクション</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> + <source>Duration</source> + <translation type="obsolete">有効期間</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> + <source>Node</source> + <translation type="obsolete">ノード</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Enabled</source> + <translation type="obsolete">有効</translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="18"/> + <source>Hits</source> + <translation type="unfinished">回数</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>Protocol</source> + <translation type="obsolete">プロトコル</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="313"/> + <source>Not running</source> + <translation type="unfinished">停止中</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="314"/> + <source>Disabled</source> + <translation type="unfinished">無効</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="315"/> + <source>Running</source> + <translation type="unfinished">実行中</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="636"/> + <source>OpenSnitch Network Statistics {0}</source> + <translation type="unfinished">OpenSnitch ネットワークモニター {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="638"/> + <source>OpenSnitch Network Statistics for {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="954"/> + <source>Disable</source> + <translation type="unfinished">無効化</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="956"/> + <source>Enable</source> + <translation type="unfinished">有効化</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="959"/> + <source>Duplicate</source> + <translation type="unfinished">複製</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="960"/> + <source>Edit</source> + <translation type="unfinished">編集</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="961"/> + <source>Delete</source> + <translation type="unfinished">削除</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1189"/> + <source> Your are about to delete this rule. </source> + <translation type="unfinished"> このルールを消去しようとしています。 </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> + <source> Are you sure?</source> + <translation type="unfinished"> 宜しいですか?</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1248"/> + <source>Rule not found by that name and node</source> + <translation type="unfinished">該当するルールが見つかりませんでした</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2581"/> + <source>Save as CSV</source> + <translation type="unfinished">CSVファイルに保存</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1301"/> + <source><b>Error:</b><br><br></source> + <comment>{0}</comment> + <translation type="unfinished"><b>エラーr:</b><br><br></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1308"/> + <source>Warning:</source> + <translation type="unfinished">警告:</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="940"/> + <source>Allow</source> + <translation type="unfinished">許可</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="941"/> + <source>Deny</source> + <translation type="unfinished">拒否</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="945"/> + <source>Always</source> + <translation type="unfinished">常に</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="946"/> + <source>Until reboot</source> + <translation type="unfinished">再起動するまで</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> + <source> You are about to delete this rule. </source> + <translation type="unfinished"> このルールを削除しようとしています。 </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> + <source>Name</source> + <comment>xxxxx</comment> + <translation type="obsolete">名前</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> + <source>Name</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">名前</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Address</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">アドレス</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> + <source>Status</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">状態</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> + <source>Hostname</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">ホスト名</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> + <source>Version</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">バージョン</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> + <source>Rules</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">ルール</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Time</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">時間</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> + <source>Action</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">アクション</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> + <source>Duration</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">有効期間</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> + <source>Node</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">ノード</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Enabled</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">有効</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> + <source>Hits</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">回数</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>Protocol</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">プロトコル</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="287"/> + <source>Name</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished">名前</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="288"/> + <source>Address</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished">アドレス</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="289"/> + <source>Status</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished">状態</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="290"/> + <source>Hostname</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished">ホスト名</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="423"/> + <source>Version</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished">バージョン</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="420"/> + <source>Rules</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished">ルール</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Time</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished">時間</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> + <source>Action</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished">アクション</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> + <source>Duration</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished">有効期間</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> + <source>Node</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished">ノード</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Enabled</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished">有効</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="438"/> + <source>Hits</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished">回数</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> + <source>Protocol</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished">プロトコル</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Process</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished">プロセス</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>Destination</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished">宛先</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> + <source>Rule</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished">ルール</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> + <source>UserID</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished">ユーザーID</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> + <source>LastConnection</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished">最終接続</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> + <source>Args</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="obsolete">引数</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> + <source>DstIP</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished">宛先IP</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> + <source>DstHost</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished">宛先ホスト</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> + <source>DstPort</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished">宛先ポート</translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="17"/> + <source>What</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="931"/> + <source>Apply to</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="942"/> + <source>Reject</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="19"/> + <source>Network name</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="291"/> + <source>Uptime</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="421"/> + <source>Connections</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="422"/> + <source>Dropped</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="437"/> + <source>What</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> + <source>Precedence</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="776"/> + <source>New node connected</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> + <source>Description</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> + <source>Cmdline</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="406"/> + <source>Export rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="407"/> + <source>Import rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="408"/> + <source>Export events to CSV</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> + <source>Quit</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="932"/> + <source>Export</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="964"/> + <source>To clipboard</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="965"/> + <source>To disk</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2523"/> + <source>Select a directory to export rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1191"/> + <source> Your are about to delete this entry. </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1678"/> + <source> You are about to delete this node. </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1687"/> + <source><b>Error deleting node</b><br><br></source> + <comment>{0}</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2478"/> + <source>Error exporting rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2552"/> + <source>Select a directory with rules to import (JSON files)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2566"/> + <source>Rules imported fine</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="211"/> + <source>WARNING</source> + <translation type="unfinished">WARNING</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="833"/> + <source>Details</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="835"/> + <source>New</source> + <translation type="unfinished"></translation> + </message> +</context> +</TS> diff --git a/ui/i18n/locales/lt_LT/opensnitch-lt_LT.ts b/ui/i18n/locales/lt_LT/opensnitch-lt_LT.ts new file mode 100644 index 0000000..1824111 --- /dev/null +++ b/ui/i18n/locales/lt_LT/opensnitch-lt_LT.ts @@ -0,0 +1,3324 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="lt"> +<context> + <name>Dialog</name> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="34"/> + <source>opensnitch-qt</source> + <translation>opensnitch-qt</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="300"/> + <source>User ID</source> + <translation>Vartotojo ID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="334"/> + <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source> + <translation><html><head/><body><p><span style=" font-weight:600;">Įvykdyta iš</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="647"/> + <source>TextLabel</source> + <translation>TekstoEtiketė</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="437"/> + <source>Source IP</source> + <translation>Šaltinio IP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="458"/> + <source>Process ID</source> + <translation>Proceso ID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="601"/> + <source>Destination IP</source> + <translation>Paskirties IP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="622"/> + <source>Dst Port</source> + <translation>Paskirties prievadas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="702"/> + <source>from this executable</source> + <translation>iš šio vykdomojo failo</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="707"/> + <source>from this command line</source> + <translation>iš šios komandinės eilutės</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="712"/> + <source>this destination port</source> + <translation>šis paskirties prievadas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="717"/> + <source>this user</source> + <translation>šis vartotojas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="722"/> + <source>this destination ip</source> + <translation>šis paskirties ip</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="751"/> + <source>once</source> + <translation>kartą</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="756"/> + <source>30s</source> + <translation>30 sek.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="761"/> + <source>5m</source> + <translation>5 min.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="766"/> + <source>15m</source> + <translation>15 min.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="771"/> + <source>30m</source> + <translation>30 min.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="776"/> + <source>1h</source> + <translation>1 val.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="706"/> + <source>for this session</source> + <translation type="obsolete">durante esta sesión</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="786"/> + <source>forever</source> + <translation>amžinai</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="346"/> + <source>Deny</source> + <translation>Drausti</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="337"/> + <source>Allow</source> + <translation>Leisti</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="865"/> + <source>+</source> + <translation>+</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="781"/> + <source>until reboot</source> + <translation>iki perkrovimo</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="727"/> + <source>from this PID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="809"/> + <source>action</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="14"/> + <source>Firewall</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="55"/> + <source><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="320"/> + <source>Inbound</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="313"/> + <source>Outbound</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="275"/> + <source>Profile</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="375"/> + <source>Allow inbound connections to a port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="378"/> + <source>Allow service (IN)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="397"/> + <source>Exclude outbound connections to a port from being intercepted</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="406"/> + <source>Allow service (OUT)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="426"/> + <source>New rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="431"/> + <source>Close</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="14"/> + <source>Firewall rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="26"/> + <source>Node</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="38"/> + <source>Enable</source> + <translation type="unfinished">Įjungti</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="50"/> + <source>Description</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="90"/> + <source>Simple</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="154"/> + <source>Add new condition</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="177"/> + <source>Remove selected condition</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="233"/> + <source>Direction</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="248"/> + <source>IN</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="257"/> + <source>OUT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="223"/> + <source>Action</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="285"/> + <source>ACCEPT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="294"/> + <source>DROP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="303"/> + <source>REJECT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="312"/> + <source>RETURN</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="442"/> + <source>Clear</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="453"/> + <source>Delete</source> + <translation type="unfinished">Išrinti</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="464"/> + <source>Save</source> + <translation type="unfinished">Išsaugoti</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="475"/> + <source>Add</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="266"/> + <source>FORWARD</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="271"/> + <source>PREROUTING</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="276"/> + <source>POSTROUTING</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="321"/> + <source>QUEUE</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="330"/> + <source>DNAT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="335"/> + <source>SNAT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="340"/> + <source>REDIRECT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="359"/> + <source>depending on the Action (i.e.: target), the syntaxis of the parameters will vary. +Some examples: + +QUEUE -> num 0 (or 1, 2, ...) +REDIRECT, TPROXY, DNAT, SNAT, MASQUERADE: + to :22 + to 192.168.1.254:8080 + to 192.168.1.254 + to 1024-2048 (masquerade)</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>PreferencesDialog</name> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="14"/> + <source>Preferences</source> + <translation>Nuostatos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="484"/> + <source>UI</source> + <translation>Vartotojo sąsaja</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="54"/> + <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p></body></html></source> + <translation type="obsolete">Este timeout es la cuenta atrás que aparece cuando se muestra una ventana emergente</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="466"/> + <source>Default timeout</source> + <translation>Numatytasis laiko limitas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="340"/> + <source>Pop-up default duration</source> + <translation>Iššokančiojo lango numatytoji trukmė</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="866"/> + <source>Default duration</source> + <translation>Numatytoji trukmė</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="162"/> + <source>Pop-up default action</source> + <translation type="obsolete">Acción por defecto de la ventana emergente</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="483"/> + <source>Default action</source> + <translation type="obsolete">Acción por defecto</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="323"/> + <source>Default target</source> + <translation>Numatytasis tikslas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="179"/> + <source>center</source> + <translation>centre</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="184"/> + <source>top right</source> + <translation>viršuje dešinėje</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="189"/> + <source>bottom right</source> + <translation>apačioje dešinėje</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="194"/> + <source>top left</source> + <translation>viršuje kairėje</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="199"/> + <source>bottom left</source> + <translation>apačioje kairėje</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="167"/> + <source>Prompt dialog default position on screen</source> + <translation type="obsolete">Posición por defecto</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="283"/> + <source>by executable</source> + <translation>pagal vykdomąjį failą</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="288"/> + <source>by command line</source> + <translation>pagal komandinę eilutę</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="293"/> + <source>by destination port</source> + <translation>pagal paskirties prievadą</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="298"/> + <source>by destination ip</source> + <translation>pagal paskirties ip</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="303"/> + <source>by user id</source> + <translation>pagal vartotojo ID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="970"/> + <source>once</source> + <translation>kartą</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="219"/> + <source>30s</source> + <translation>30 sek.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="224"/> + <source>5m</source> + <translation>5 min.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="229"/> + <source>15m</source> + <translation>15 min.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="234"/> + <source>30m</source> + <translation>30 min.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="239"/> + <source>1h</source> + <translation>1 val.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="240"/> + <source>for this session</source> + <translation type="obsolete">durante esta sesión</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="249"/> + <source>forever</source> + <translation>amžinai</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1012"/> + <source>deny</source> + <translation>drausti</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1021"/> + <source>allow</source> + <translation>leisti</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="406"/> + <source>Disable pop-ups, only display an alert</source> + <translation type="obsolete">Išjungti iššokančius langus, rodyti tik įspėjimą</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="823"/> + <source>Nodes</source> + <translation>Mazgai</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="829"/> + <source>Process monitor method</source> + <translation>Proceso stebėjimo metodas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="863"/> + <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source> + <translation><html><head/><body><p>Numatytoji trukmė bus taikoma, kai nėra prijungtos vartotojo sąsajos.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="988"/> + <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source> + <translation><html><head/><body><p>Mazgo adresas </p><p>Numatytoji reikšmė: unix:///tmp/osui.sock (unix:// privaloma, jei tai Unix lizdas) </p><p>Tai taip pat gali būti IP adresas su prievadu: 127.0.0.1:50051</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="991"/> + <source>Address</source> + <translation>Adresas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1131"/> + <source>Default log level</source> + <translation>Numatytasis registravimo žurnale lygis</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1039"/> + <source>Version</source> + <translation>Versija</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="902"/> + <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source> + <translation><html><head/><body><p>Numatytasis veiksmas bus atliekamas, kai nėra prijungtos vartotojo sąsajos.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="846"/> + <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source> + <translation><html><head/><body><p>Žurnalo failas, į kurį įrašomi žurnalo įrašai.<br/></p><p>/dev/stdout spausdins žurnalus į standartinę išvestį.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="849"/> + <source>Log file</source> + <translation>Žurnalo failas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="578"/> + <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons.</p><p>The pop-up dialog will only contain information about the network connection.</p></body></html></source> + <translation type="obsolete">Si marcas esta opción, OpenSnitch te preguntará para Aceptar o Denegar conexiones que no tengan un PID asociado por diferentes razones. + +La ventana emergente sólo contendrá información relativa a la conexión. + +Nota: Estas conexiones no tienen por qué indicar que algo sospechoso está sucediendo. Simplemente +es que no hemos descubierto el PID (por ejemplo conexiones que no se originan en la máquina, o paquetes en mal estado).</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="581"/> + <source>Intercept Unknown Connections</source> + <translation type="obsolete">Interceptar conexiones desconocidas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="921"/> + <source>HostName</source> + <translation>Kompiuterio vardas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1090"/> + <source>unix:///tmp/osui.sock</source> + <translation>unix:///tmp/osui.sock</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="975"/> + <source>until restart</source> + <translation>iki perkrovimo</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="980"/> + <source>always</source> + <translation>visada</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1102"/> + <source>/var/log/opensnitchd.log</source> + <translation>/var/log/opensnitchd.log</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1107"/> + <source>/dev/stdout</source> + <translation>/dev/stdout</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="879"/> + <source>Apply configuration to all nodes</source> + <translation>Taikyti konfigūraciją visiems mazgams</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1146"/> + <source>Database</source> + <translation>Duomenų bazė</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1181"/> + <source>In memory</source> + <translation>Atmintyje</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1186"/> + <source>File</source> + <translation>Failas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1482"/> + <source>Close</source> + <translation>Uždaryti</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1493"/> + <source>Apply</source> + <translation>Taikyti</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1504"/> + <source>Save</source> + <translation>Išsaugoti</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="244"/> + <source>until reboot</source> + <translation>iki perkrovimo</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1200"/> + <source>Database type</source> + <translation>Duomenų bazės tipas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1207"/> + <source>Select</source> + <translation>Pasirinkti</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="83"/> + <source>Pop-ups default options</source> + <translation type="obsolete">Opciones por defecto de las ventanas emergentes</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="367"/> + <source>Pop-ups default position on screen</source> + <translation type="obsolete">Posición en pantalla</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="102"/> + <source><html><head/><body><p>The advanced view allows you to apply more filters on a connection</p><p>when a pop-up appears.</p></body></html></source> + <translation type="obsolete">La vista avanzada permite filtrar conexiones por más parámetros</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="359"/> + <source>Show advanced view by default</source> + <translation>Rodyti išplėstinį rodinį automatiškai</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="653"/> + <source>Action</source> + <translation>Veiksmas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="375"/> + <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source> + <translation><html><head/><body><p>Jei pažymėta, iššokantys langai bus rodomi su aktyviu išplėstiniu rodiniu.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="343"/> + <source>Duration</source> + <translation>Trukmė</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="263"/> + <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source> + <translation><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="266"/> + <source>Filter connections also by:</source> + <translation>Taip pat filtruoti prisijungimus pagal:</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="362"/> + <source>If checked, this field will be checked when a pop-up is displayed</source> + <translation type="obsolete">Si lo seleccionas, este campo se usará para filtrar las conexiones</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="81"/> + <source>User ID</source> + <translation>Vartotojo ID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="97"/> + <source>Destination port</source> + <translation>Paskirties prievadas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="113"/> + <source>Destination IP</source> + <translation>Paskirties IP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="463"/> + <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source> + <translation><html><head/><body><p>Šis laiko limitas yra atgalinis laikmatis, kurį matote, kai rodomas iššokantis dialogo langas.</p><p>Jei į iššokantį dialogo langą neatsakoma, taikomos numatytosios parinktys.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="356"/> + <source>The advanced view allows you to easily select multiple fields to filter connections</source> + <translation>Išplėstinis vaizdas leidžia lengvai pasirinkti kelis laukus, kad būtų galima filtruoti prisijungimus</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="110"/> + <source>If checked, this field will be selected when a pop-up is displayed</source> + <translation>Jei pažymėta, šis laukas bus pasirinktas, kai bus rodomas iššokantis langas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="159"/> + <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source> + <translation><html><head/><body><p>Iššokančio lango numatytasis veiksmas.</p><p>Kai ruošiamasi užmegzti naują išeinantį ryšį, šis veiksmas bus pasirinktas pagal numatytuosius nustatymus, todėl, jei suveiks laiko limitas, bus taikoma ši parinktis.</p><p><br/></p><p>Kai iššokančiame lange prašoma leisti arba neleisti prisijungti: </p><p>1. Nauji išeinantys ryšiai neleidžiami.</p><p>2. Žinomi ryšiai leidžiami arba neleidžiami pagal vartotojo nustatytas taisykles.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="905"/> + <source>Default action when the GUI is disconnected</source> + <translation>Default action when the GUI is disconnected</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1001"/> + <source>Debug invalid connections</source> + <translation>Debug invalid connections</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="39"/> + <source>Pop-ups</source> + <translation>Iššokantys langai</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="64"/> + <source>Default options</source> + <translation>Numatytosios parinktys</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="330"/> + <source>Default position on screen</source> + <translation>Numatytoji padėtis ekrane</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="769"/> + <source>any temporary rules</source> + <translation>bet kokios laikinos taisyklės</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="478"/> + <source><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="481"/> + <source>Don't save rules of duration</source> + <translation type="obsolete">Don't save rules of duration</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="463"/> + <source>Show events columns</source> + <translation type="obsolete">Mostrar columnas de la pestaña Eventos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="589"/> + <source>Time</source> + <translation>Laikas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="669"/> + <source>Destination</source> + <translation>Paskirties vieta</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="637"/> + <source>Protocol</source> + <translation>Protokolas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="685"/> + <source>Process</source> + <translation>Procesas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="605"/> + <source>Rule</source> + <translation>Taisyklė</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="621"/> + <source>Node</source> + <translation>Mazgas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="577"/> + <source>Events tab columns</source> + <translation>Events tab columns</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="308"/> + <source>by PID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="496"/> + <source>Desktop notifications</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="514"/> + <source>Use system notifications</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="530"/> + <source>Use Qt notifications</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="559"/> + <source>Test</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="716"/> + <source>System</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="708"/> + <source>Theme</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="998"/> + <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1294"/> + <source>minutes</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1326"/> + <source>Minutes between events purges</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1352"/> + <source>days</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1365"/> + <source>Maximum days of events to keep</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="147"/> + <source>reject</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="473"/> + <source>Disable pop-ups, only display a notification</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="695"/> + <source>Command line</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="748"/> + <source>Rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="756"/> + <source>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI. + +Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="761"/> + <source>Don't save/Delete rules of duration</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="779"/> + <source>30s or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="784"/> + <source>5m or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="789"/> + <source>15m or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="794"/> + <source>30m or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="799"/> + <source>1h or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="724"/> + <source>Language</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>ProcessDetailsDialog</name> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="14"/> + <source>Process details</source> + <translation>Proceso informacija</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="61"/> + <source>loading...</source> + <translation>įkeliama…</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="81"/> + <source>CWD: loading...</source> + <translation>CWD: loading...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="93"/> + <source>mem stats: loading...</source> + <translation>mem stats: loading...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="121"/> + <source>Status</source> + <translation>Būsena</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="135"/> + <source>Open files</source> + <translation>Atidaryti failus</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="149"/> + <source>I/O Statistics</source> + <translation>I/O Statistics</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="163"/> + <source>Memory mapped files</source> + <translation>Memory mapped files</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="177"/> + <source>Stack</source> + <translation>Stack</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="191"/> + <source>Environment variables</source> + <translation>Aplinkos kintamieji</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="210"/> + <source>Application pids</source> + <translation>Application pids</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="240"/> + <source>Start or stop monitoring this process</source> + <translation>Pradėti arba sustabdyti šio proceso stebėjimą</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="256"/> + <source>Close</source> + <translation>Uždaryti</translation> + </message> +</context> +<context> + <name>RulesDialog</name> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="20"/> + <source>Rule</source> + <translation>Taisyklė</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> + <source>Node</source> + <translation>Mazgas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> + <source>Apply rule to all nodes</source> + <translation>Taikyti taisyklę visiems mazgams</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="379"/> + <source>From this command line</source> + <translation>Iš šios komandinės eilutės</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="472"/> + <source>From this executable</source> + <translation>Iš šio vykdomojo failo</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="56"/> + <source>Action</source> + <translation>Veiksmas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="610"/> + <source>To this IP / Network</source> + <translation>Į šį IP / tinklą</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="97"/> + <source>once</source> + <translation>kartą</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="102"/> + <source>30s</source> + <translation type="obsolete">30 sek.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="107"/> + <source>5m</source> + <translation type="obsolete">5 min.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="112"/> + <source>15m</source> + <translation type="obsolete">15 min.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="117"/> + <source>30m</source> + <translation type="obsolete">30 min.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="122"/> + <source>1h</source> + <translation type="obsolete">1 val.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="230"/> + <source>until restart</source> + <translation type="obsolete">hasta reiniciar (el servicio)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="132"/> + <source>always</source> + <translation>visada</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="902"/> + <source>To this port</source> + <translation>Į šį prievadą</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="372"/> + <source>From this user ID</source> + <translation>Iš šio vartotojo ID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/> + <source>Commas or spaces are not allowed to specify multiple domains. + +Use regular expressions instead: +.*(opensnitch|duckduckgo).com +.*\.google.com + +or a single domain: +www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... +gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source> + <translation>Kelių domenų nurodyti kableliais ar tarpais neleidžiama. + +Vietoj jų naudokite reguliariąsias išraiškas: +.*(opensnitch|duckduckgo).com +.*\.google.com + +arba vieną domeną: +www.gnu.org - atitiks tik www.gnu.org, nei ftp.gnu.org, nei www2.gnu.org, ... +gnu.org - atitiks tik gnu.org, www.gnu.org, ftp.gnu.org, ...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="603"/> + <source>www.domain.org, .*\.domain.org</source> + <translation>www.domenas.org, .*\.domenas.org</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="526"/> + <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source> + <translation><html><head/><body><p>Leidžiama naudoti tik TCP, UDP arba UDPLITE</p><p>Galite naudoti regexp, t. y.: ^(TCP|UDP)$</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="532"/> + <source>TCP</source> + <translation>TCP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="411"/> + <source>UDP</source> + <translation type="obsolete">UDP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="416"/> + <source>UDPLITE</source> + <translation type="obsolete">UDPLITE</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="421"/> + <source>TCP6</source> + <translation type="obsolete">TCP6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="426"/> + <source>UDP6</source> + <translation type="obsolete">UDP6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="431"/> + <source>UDPLITE6</source> + <translation type="obsolete">UDPLITE6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="760"/> + <source>You can specify a single IP: +- 192.168.1.1 + +or a regular expression: +- 192\.168\.1\.[0-9]+ + +multiple IPs: +- ^(192\.168\.1\.1|172\.16\.0\.1)$ + +You can also specify a subnet: +- 192.168.1.0/24 + +Note: Commas or spaces are not allowed to separate IPs or networks.</source> + <translation>Galite nurodyti vieną IP adresą: +- 192.168.1.1 + +arba reguliarią išraišką: +- 192\.168\.1\.[0-9]+ + +kelių IP adresų: +- ^(192\.168\.1\.1|172\.16\.0\.1)$ + +Taip pat galite nurodyti potinklį: +- 192.168.1.0/24 + +Pastaba: kableliais ar tarpais atskirti IP ar tinklų negalima.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="532"/> + <source>LAN</source> + <translation type="obsolete">LAN</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="537"/> + <source>127.0.0.0/8</source> + <translation type="obsolete">127.0.0.0/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="542"/> + <source>192.168.0.0/24</source> + <translation type="obsolete">192.168.0.0/24</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="547"/> + <source>192.168.1.0/24</source> + <translation type="obsolete">192.168.1.0/24</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="552"/> + <source>192.168.2.0/24</source> + <translation type="obsolete">192.168.2.0/24</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="557"/> + <source>192.168.0.0/16</source> + <translation type="obsolete">192.168.0.0/16</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="562"/> + <source>169.254.0.0/16</source> + <translation type="obsolete">169.254.0.0/16</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="567"/> + <source>172.16.0.0/12</source> + <translation type="obsolete">172.16.0.0/12</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="572"/> + <source>10.0.0.0/8</source> + <translation type="obsolete">10.0.0.0/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="577"/> + <source>::1/128</source> + <translation type="obsolete">::1/128</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="582"/> + <source>fc00::/7</source> + <translation type="obsolete">fc00::/7</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="587"/> + <source>ff00::/8</source> + <translation type="obsolete">ff00::/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/> + <source>fe80::/10</source> + <translation type="obsolete">fe80::/10</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="597"/> + <source>fd00::/8</source> + <translation type="obsolete">fd00::/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="89"/> + <source>Duration</source> + <translation>Trukmė</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="633"/> + <source>Protocol</source> + <translation>Protokolas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="750"/> + <source>To this host</source> + <translation>To this host</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="151"/> + <source>Deny</source> + <translation>Drausti</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="191"/> + <source>Allow</source> + <translation>Leisti</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="245"/> + <source>Name</source> + <translation type="unfinished">Pavadinimas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="207"/> + <source>Enable</source> + <translation>Įjungti</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="238"/> + <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them. + +000-allow-localhost +001-deny-broadcast +...</source> + <translation>Taisyklės tikrinamos abėcėlės tvarka, todėl galite jas atitinkamai pavadinę nustatyti jų prioritetus. + +000-allow-localhost +001-deny-broadcast +...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="935"/> + <source>leave blank to autocreate</source> + <translation type="obsolete">palikite tuščią, kad sukurtumėte automatiškai</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="214"/> + <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one. + +You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example: + +[x] Priority - 000-priority-rule +[ ] Priority - 001-less-priority-rule</source> + <translation>Jei pažymėta, ši taisyklė bus viršesnė už kitas taisykles. Po šios taisyklės nebus tikrinamos jokios kitos taisyklės. + +Taisyklę turite pavadinti taip, kad ji būtų tikrinama pirma, nes taisyklės tikrinamos abėcėlės tvarka. Pavyzdžiui: + +[x] Prioritetas - 000-prioritetinė-taisyklė +[ ] Prioritetas - 001-mažesnio prioriteto taisyklė</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="222"/> + <source>Priority rule</source> + <translation>Prioritetinė taisyklė</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1092"/> + <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source> + <translation><html><head/><body><p>Pagal numatytuosius nustatymus taisyklių lauke neribojamos didžiosios raidės, t. y., jei procesas bando prisijungti prie gOOgle.CoM, o jūs turite taisyklę Deny .*google.com, prisijungimas bus užblokuotas.<br/></p><p>Jei pažymėsite šį laukelį, turėsite nurodyti tikslų (domeną, vykdomąjį failą, komandinę eilutę), kurią norite filtruoti.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1095"/> + <source>Case-sensitive</source> + <translation>Case-sensitive</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="686"/> + <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="127"/> + <source>until reboot</source> + <translation>iki perkrovimo</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="980"/> + <source>To this list of domains</source> + <translation>Į šį domenų sąrašą</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="539"/> + <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Pasirinkite katalogą su blokuojamų arba leidžiamų domenų sąrašais.</p><p>Į tą katalogą įdėkite failus su bet kokiu plėtiniu, kuriuose yra domenų sąrašai.</p><p><br/>Kiekvieno sąrašo įrašo formatas yra toks (pagrindinio kompiuterio formatas): </p><p>127.0.0.0.1 www.domain.com</p><p>arba </p><p>0.0.0.0.0 www.domenas.com</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="148"/> + <source>Deny will just discard the connection</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="165"/> + <source>Reject will drop the connection, and kill the socket that initiated it</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="168"/> + <source>Reject</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="185"/> + <source>Allow will allow the connection</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="346"/> + <source>Applications</source> + <translation type="unfinished">Programos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="355"/> + <source><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="365"/> + <source>Is regular expression</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="389"/> + <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="399"/> + <source>From this PID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> + <source>is regular expression</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="491"/> + <source>Network</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="932"/> + <source>List of domains/IPs</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="938"/> + <source>To this list of network ranges</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="945"/> + <source>To this list of IPs</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="971"/> + <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1006"/> + <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1034"/> + <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1049"/> + <source>To this list of domains +(regular expressions)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1076"/> + <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1086"/> + <source>More</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="566"/> + <source>ICMP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="571"/> + <source>ICMP6</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="576"/> + <source>SCTP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="581"/> + <source>SCTP6</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="863"/> + <source>Network interface</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1102"/> + <source>Don't log connections that match this rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1105"/> + <source>Don't log connections</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1151"/> + <source>Description...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="743"/> + <source>From this IP / Network</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="872"/> + <source>From this port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="918"/> + <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>StatsDialog</name> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="34"/> + <source>OpenSnitch Network Statistics</source> + <translation>OpenSnitch tinklo statistika</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="284"/> + <source>Save to CSV</source> + <translation type="obsolete">Įrašyti į CSV.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="294"/> + <source>Ctrl+S</source> + <translation type="obsolete">Ctrl+S</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="333"/> + <source>Create a new rule</source> + <translation>Sukurti naują taisyklę</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="376"/> + <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source> + <translation><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">pagrindinio kompiuterio vardas - 192.168.1.1</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="413"/> + <source>Status</source> + <translation>Statusas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1793"/> + <source>-</source> + <translation>-</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="451"/> + <source>Start or Stop interception</source> + <translation>Pradėti arba sustabdyti perėmimą</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="496"/> + <source>Events</source> + <translation>Įvykiai</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="94"/> + <source>Filter</source> + <translation>Filtras</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="107"/> + <source>Allow</source> + <translation>Leisti</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="116"/> + <source>Deny</source> + <translation type="unfinished">Atmesti</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="143"/> + <source>Ex.: firefox</source> + <translation>Pvz.: firefox</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="205"/> + <source>50</source> + <translation>50</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="210"/> + <source>100</source> + <translation>100</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="215"/> + <source>200</source> + <translation>200</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="220"/> + <source>300</source> + <translation>300</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="826"/> + <source>Nodes</source> + <translation>Mazgai</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1700"/> + <source>Rules</source> + <translation>Taisyklės</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="995"/> + <source>enable</source> + <translation>įjungti</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="684"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Name column to view details of a rule)</span></p></body></html></source> + <translation type="obsolete">(doble click en la columna Nombre para ver los detalles)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="782"/> + <source>Application rules</source> + <translation>Application rules</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="936"/> + <source>Permanent</source> + <translation>Nuolatinė</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="945"/> + <source>Temporary</source> + <translation>Laikina</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1063"/> + <source>Hosts</source> + <translation>Hosts</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1364"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click to view details of an item)</span></p></body></html></source> + <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(dukart spustelėkite, kad peržiūrėtumėte detalią informaciją apie elementą)</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1153"/> + <source>Applications</source> + <translation>Programos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1266"/> + <source>Addresses</source> + <translation>Adresai</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1356"/> + <source>Ports</source> + <translation>Prievadai</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1440"/> + <source>Users</source> + <translation>Vartotojai</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1544"/> + <source>Connections</source> + <translation>Connections</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1596"/> + <source>Dropped</source> + <translation>Dropped</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1648"/> + <source>Uptime</source> + <translation>Veikimo laikas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1767"/> + <source>Version</source> + <translation>Versija</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="233"/> + <source>Delete all intercepted events</source> + <translation type="unfinished">Ištrinti visus perimtus įvykius</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1022"/> + <source>Edit rule</source> + <translation>Redaguoti taisyklę</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1039"/> + <source>Delete rule</source> + <translation>Ištrinti taisyklę</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="699"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click on a row to view details of a rule)</span></p></body></html></source> + <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(dukart spustelėkite, kad peržiūrėtumėte detalią informaciją apie taisyklę)</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="927"/> + <source>All applications</source> + <translation>Visos programos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="125"/> + <source>Reject</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="180"/> + <source>0</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="637"/> + <source>Delete this node</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="653"/> + <source>Show the preferences of this node</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="669"/> + <source>Start or stop interception of this node</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="777"/> + <source>2</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="954"/> + <source>System rules</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>contextual_menu</name> + <message> + <location filename="../../../opensnitch/service.py" line="47"/> + <source>Statistics</source> + <translation>Statistika</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="50"/> + <source>Help</source> + <translation>Pagalba</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="51"/> + <source>Close</source> + <translation>Uždaryti</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="48"/> + <source>Enable</source> + <translation>Įjungti</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="49"/> + <source>Disable</source> + <translation>Išjungti</translation> + </message> +</context> +<context> + <name>firewall</name> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="91"/> + <source>Configuration applied.</source> + <translation type="unfinished">Konfigūracija pritaikyta.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="404"/> + <source>Error: {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="193"/> + <source>Applying changes...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="230"/> + <source>Error getting INPUT chain policy</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="237"/> + <source>Error getting OUTPUT chain policy</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="290"/> + <source>In order to configure firewall rules from the GUI, we need to use 'nftables' instead of 'iptables'</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="304"/> + <source>Enabling firewall...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="306"/> + <source>Disabling firewall...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="71"/> + <source>Dest Port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="72"/> + <source>Source Port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="73"/> + <source>Dest IP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="74"/> + <source>Source IP</source> + <translation type="unfinished">Šaltinio IP</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="75"/> + <source>Input interface</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="76"/> + <source>Output interface</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="77"/> + <source>Set conntrack mark</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="78"/> + <source>Match conntrack mark</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="79"/> + <source>Match conntrack state(s)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="80"/> + <source>Set mark on packet</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="81"/> + <source>Match packet information</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="87"/> + <source>Bandwidth quotas</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="89"/> + <source>Rate limit connections</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="373"/> + <source>Your protobuf version is incompatible, you need to install protobuf 3.8.0 or superior +(pip3 install --ignore-installed protobuf==3.8.0)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="397"/> + <source>Rule deleted</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="401"/> + <source>Rule added</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="420"/> + <source>You can use ',' or '-' to specify multiple ports/IPs or ranges/values:<br><br>ports: 22 or 22,443 or 50000-60000<br>IPs: 192.168.1.1 or 192.168.1.30-192.168.1.130<br>Values: echo-reply,echo-request<br>Values: new,established,related</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="440"/> + <source>Deleting rule, wait</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="443"/> + <source>Error updating rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="483"/> + <source>Adding rule, wait</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="492"/> + <source><select a statement></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="787"/> + <source>Equal</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="788"/> + <source>Not equal</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="789"/> + <source>Greater or equal than</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="790"/> + <source>Greater than</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="791"/> + <source>Less or equal than</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="792"/> + <source>Less than</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1350"/> + <source>Firewall rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="885"/> + <source>Simple</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="890"/> + <source>Advanced</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1046"/> + <source>This rule is not supported yet.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1111"/> + <source>Exclude service</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1123"/> + <source>Allow inbound connections to the selected port.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1125"/> + <source>Allow outbound connections to the selected port.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1201"/> + <source>select a statement.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1217"/> + <source>value cannot be 0 or empty.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1229"/> + <source>the value format is 1024/kbytes (or bytes, mbytes, gbytes)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1240"/> + <source>the value format is 1024/kbytes/second (or bytes, mbytes, gbytes)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1243"/> + <source>rate-limit not valid, use: bytes, kbytes, mbytes or gbytes.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1245"/> + <source>time-limit not valid, use: second, minute, hour or day</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1293"/> + <source>port not valid.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="108"/> + <source> +Supported formats: + + - Simple: 23 + - Ranges: 80-1024 + - Multiple ports: 80,443,8080 +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="134"/> + <source> +Supported formats: + + - Simple: 1.2.3.4 + - IP ranges: 1.2.3.100-1.2.3.200 + - Network ranges: 1.2.3.4/24 +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="147"/> + <source>Match input interface. Regular expressions not allowed.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="154"/> + <source>Match output interface. Regular expressions not allowed.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="161"/> + <source>Set a conntrack mark on the connection, in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="171"/> + <source>Match a conntrack mark of the connection, in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="178"/> + <source>Match conntrack states. + +Supported formats: + - Simple: new + - Multiple states separated by commas: related,new +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="193"/> + <source> +Match packet's metainformation. + +Value must be in decimal format, except for the "l4proto" option. +For l4proto it can be a lower case string, for example: + tcp + udp + icmp, + etc + +If the value is decimal for protocol or lproto, it'll use it as the code of +that protocol. +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="213"/> + <source>Set a mark on the packet matching the specified conditions. The value is in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="221"/> + <source> +Match ICMP codes. + +Supported formats: + - Simple: echo-request + - Multiple separated by commas: echo-request,echo-reply +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="234"/> + <source> +Match ICMPv6 codes. + +Supported formats: + - Simple: echo-request + - Multiple separated by commas: echo-request,echo-reply +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="247"/> + <source>Print a message when this rule matches a packet.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="254"/> + <source> +Apply quotas on connections. + +For example when: + - "quota over 10/mbytes" -> apply the Action defined (DROP) + - "quota until 10/mbytes" -> apply the Action defined (ACCEPT) + +The value must be in the format: VALUE/UNITS, for example: + - 10mbytes, 1/gbytes, etc +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="286"/> + <source> +Apply limits on connections. + +For example when: + - "limit over 10/mbytes/minute" -> apply the Action defined (DROP, ACCEPT, etc) + (When there're more than 10MB per minute, apply an Action) + + - "limit until 10/mbytes/hour" -> apply the Action defined (ACCEPT) + +The value must be in the format: VALUE/UNITS/TIME, for example: + - 10/mbytes/minute, 1/gbytes/hour, etc +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="607"/> + <source>num</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="621"/> + <source>to</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>menu_close</name> + <message> + <location filename="../../../opensnitch/service.py" line="131"/> + <source>Close</source> + <translation type="obsolete">Cerrar</translation> + </message> +</context> +<context> + <name>menu_help</name> + <message> + <location filename="../../../opensnitch/service.py" line="126"/> + <source>Help</source> + <translation type="obsolete">Ayuda</translation> + </message> +</context> +<context> + <name>menu_statistics</name> + <message> + <location filename="../../../opensnitch/service.py" line="120"/> + <source>Statistics</source> + <translation type="obsolete">Eventos</translation> + </message> +</context> +<context> + <name>messages</name> + <message> + <location filename="../../../opensnitch/service.py" line="281"/> + <source>Info</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="285"/> + <source>Error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="289"/> + <source>Warning</source> + <translation type="unfinished">Įspėjimas</translation> + </message> +</context> +<context> + <name>notifications</name> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="654"/> + <source>System notifications are not available, you need to install python3-notify2.</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>popups</name> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="115"/> + <source>Allow</source> + <translation>Leisti</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="114"/> + <source>Deny</source> + <translation>Drausti</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/> + <source>forever</source> + <translation>amžinai</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="331"/> + <source>Outgoing connection</source> + <translation>Outgoing connection</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="336"/> + <source>Process launched from:</source> + <translation>Procesas paleistas iš:</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="377"/> + <source>from this command line</source> + <translation>iš šios komandinės eilutės</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="373"/> + <source>from this executable</source> + <translation>iš šio vykdomojo failo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="208"/> + <source>Unknown process</source> + <translation type="obsolete">Proceso no encontrado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="50"/> + <source>until reboot</source> + <translation>iki perkrovimo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="379"/> + <source>to port {0}</source> + <translation>į prievadą {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="222"/> + <source><b>%s</b> is connecting to <b>%s</b> on %s port %d</source> + <translation type="obsolete"><b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="228"/> + <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source> + <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="442"/> + <source>to {0}</source> + <translation>į {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="382"/> + <source>from user {0}</source> + <translation>iš vartotojo {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="399"/> + <source>to {0}.*</source> + <translation>į {0}.*</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="452"/> + <source>to *.{0}</source> + <translation>į *.{0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="378"/> + <source>to *{0}</source> + <translation type="obsolete">į *{0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="486"/> + <source><b>Remote</b> process %s running on <b>%s</b></source> + <translation><b>Nuotolinis</b> procesas %s veikia <b>%s</b></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="490"/> + <source>is connecting to <b>%s</b> on %s port %d</source> + <translation>jungiasi prie <b>%s</b> per %s prievadą %d</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="502"/> + <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source> + <translation>bando išspręsti <b>%s</b> per %s, %s prievadą %d</translation> + </message> + <message> + <location filename="../../../opensnitch/notifications.py" line="122"/> + <source>New outgoing connection</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="386"/> + <source>from this PID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="116"/> + <source>Reject</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="497"/> + <source>is connecting to <b>%s</b>, %s</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/notifications.py" line="42"/> + <source>Open</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>popups2</name> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="254"/> + <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source> + <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> + </message> +</context> +<context> + <name>preferences</name> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="171"/> + <source>Exception saving config: %s</source> + <translation type="obsolete">Error al guarda la configuración: %s</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="177"/> + <source>Applying configuration on %s ...</source> + <translation type="obsolete">Aplicando configuración en %s ...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="292"/> + <source>Server address can not be empty</source> + <translation>Serverio adresas negali būti tuščias</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="227"/> + <source>Error loading %s configuration</source> + <translation type="obsolete">Error al cargar la configuración %s</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="568"/> + <source>Configuration applied.</source> + <translation>Konfigūracija pritaikyta.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="257"/> + <source>Error applying configuration: %s</source> + <translation type="obsolete">Error al aplicar la configuración: %s</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="386"/> + <source>Exception saving config: {0}</source> + <translation>Išimtis išsaugant konfigūraciją: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="500"/> + <source>Applying configuration on {0} ...</source> + <translation>Taikoma konfigūracija {0} ...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="321"/> + <source>Error loading {0} configuration</source> + <translation>Įkeliant {0} konfigūraciją įvyko klaida</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="570"/> + <source>Error applying configuration: {0}</source> + <translation>Taikant konfigūraciją įvyko klaida: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> + <source>Warning</source> + <translation>Įspėjimas</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> + <source>You must select a file for the database<br>or choose "In memory" type.</source> + <translation>Turite pasirinkti duomenų bazės failą<br>arba pasirinkite tipą „Atmintyje“.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="401"/> + <source>DB type changed</source> + <translation>DB tipas pakeistas</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="38"/> + <source>Restart the GUI in order effects to take effect</source> + <translation>Restart the GUI in order effects to take effect</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="607"/> + <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source> + <translation>Užveskite pelės žymeklį virš teksto, kad būtų rodoma pagalba<br><br>Nepamirškite apsilankyti wiki: <a href="{0}">{0}</a></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="466"/> + <source>System</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="187"/> + <source>Themes not available. Install qt-material: pip3 install qt-material</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> + <source>UI theme changed</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> + <source>Restart the GUI in order to apply the new theme</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="388"/> + <source>There're no nodes connected</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="508"/> + <source>Ok</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="520"/> + <source>Exception saving node config {0}: {1}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="164"/> + <source>System default</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="433"/> + <source>Language changed</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>proc_details</name> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="100"/> + <source><b>Error loading process information:</b> <br><br> + +</source> + <translation><b>Klaida įkeliant proceso informaciją:</b><br><br> + +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="119"/> + <source><b>Error stopping monitoring process:</b><br><br></source> + <translation><b>Sustabdant stebėjimo procesą įvyko klaida:</b><br><br></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="159"/> + <source>loading...</source> + <translation>įkeliama…</translation> + </message> +</context> +<context> + <name>rules</name> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="228"/> + <source>There're no nodes connected.</source> + <translation>Nėra prijungtų mazgų.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="271"/> + <source>Rule applied.</source> + <translation>Taisyklė pritaikyta.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="123"/> + <source>Error applying rule: %s</source> + <translation type="obsolete">Error al aplicar la regla: %s</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="641"/> + <source>protocol can not be empty, or uncheck it</source> + <translation>protokolas negali būti tuščias arba panaikinkite jo žymėjimą</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="655"/> + <source>Protocol regexp error</source> + <translation type="unfinished">Protokolo regexp klaida</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="659"/> + <source>process path can not be empty</source> + <translation>proceso kelias negali būti tuščias</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="673"/> + <source>Process path regexp error</source> + <translation>Process path regexp error</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="677"/> + <source>command line can not be empty</source> + <translation>komandinė eilutė negali būti tuščia</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="691"/> + <source>Command line regexp error</source> + <translation>Command line regexp error</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="731"/> + <source>Dest port can not be empty</source> + <translation>Paskirties prievadas negali būti tuščias</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="745"/> + <source>Dst port regexp error</source> + <translation>Dst port regexp error</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="749"/> + <source>Dest host can not be empty</source> + <translation>Dest host can not be empty</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="763"/> + <source>Dst host regexp error</source> + <translation>Dst host regexp error</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="805"/> + <source>Dest IP/Network can not be empty</source> + <translation>Dest IP/Network can not be empty</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="831"/> + <source>Dst IP regexp error</source> + <translation>Dst IP regexp error</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="843"/> + <source>User ID can not be empty</source> + <translation>Vartotojo ID negali būti tuščias</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="857"/> + <source>User ID regexp error</source> + <translation>User ID regexp error</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="273"/> + <source>Error applying rule: {0}</source> + <translation>Klaida taikant taisyklę: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="931"/> + <source>Lists field cannot be empty</source> + <translation>Sąrašų laukas negali būti tuščias</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="933"/> + <source>Lists field must be a directory</source> + <translation>Lists field must be a directory</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="976"/> + <source><b>Rule not supported</b></source> + <translation><b>Taisyklė nepalaikoma</b></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="539"/> + <source><b>Error loading rule</b></source> + <translation><b>Klaida įkeliant taisyklę</b></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="245"/> + <source>There's already a rule with this name.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="861"/> + <source>PID field can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="875"/> + <source>PID field regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="963"/> + <source>Select at least one field.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="695"/> + <source>Network interface can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="709"/> + <source>Network interface regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="713"/> + <source>Source port can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="727"/> + <source>Source port regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="767"/> + <source>Source IP/Network can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="793"/> + <source>Source IP regexp error</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>stats</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="313"/> + <source>Not running</source> + <translation>Neveikia</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="314"/> + <source>Disabled</source> + <translation>Išjungta</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="315"/> + <source>Running</source> + <translation>Veikia</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="412"/> + <source>OpenSnitch Network Statistics</source> + <translation type="obsolete">Eventos de OpenSnitch</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="414"/> + <source>OpenSnitch Network Statistics for</source> + <translation type="obsolete">Eventos de OpenSnitch de</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1189"/> + <source> Your are about to delete this rule. </source> + <translation> Ketinate ištrinti šią taisyklę. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> + <source> Are you sure?</source> + <translation> Ar esate tuo tikras?</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="636"/> + <source>OpenSnitch Network Statistics {0}</source> + <translation>OpenSnitch tinklo statistika {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="638"/> + <source>OpenSnitch Network Statistics for {0}</source> + <translation>{0} OpenSnitch tinklo statistika</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> + <source>Name</source> + <translation type="obsolete">Nombre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Address</source> + <translation type="obsolete">Dirección</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> + <source>Status</source> + <translation type="obsolete">Estado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> + <source>Hostname</source> + <translation type="obsolete">Hostname</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> + <source>Version</source> + <translation type="obsolete">Versión</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="834"/> + <source>Rules</source> + <translation type="unfinished">Reglas</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Time</source> + <translation type="obsolete">Hora</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="875"/> + <source>Action</source> + <translation type="unfinished">Acción</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> + <source>Duration</source> + <translation type="obsolete">Duración</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> + <source>Node</source> + <translation type="obsolete">Nodo</translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="18"/> + <source>Hits</source> + <translation type="unfinished">Total</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>Protocol</source> + <translation type="obsolete">Protocolo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2581"/> + <source>Save as CSV</source> + <translation>Išsaugoti kaip CSV</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Enabled</source> + <translation type="obsolete">Habilitado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="961"/> + <source>Delete</source> + <translation>Išrinti</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="948"/> + <source>always</source> + <translation type="obsolete">siempre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="580"/> + <source><b>Error:</b><br><br>{0}</source> + <translation type="obsolete"><b>Error:</b><br><br>{0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="954"/> + <source>Disable</source> + <translation>Išjungti</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="956"/> + <source>Enable</source> + <translation>Įjungti</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="959"/> + <source>Duplicate</source> + <translation>Duplicate</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="960"/> + <source>Edit</source> + <translation>Redaguoti</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1248"/> + <source>Rule not found by that name and node</source> + <translation>Taisyklė pagal šį pavadinimą ir mazgą nerasta</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1301"/> + <source><b>Error:</b><br><br></source> + <comment>{0}</comment> + <translation><b>Klaida:</b><br><br></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1308"/> + <source>Warning:</source> + <translation>Įspėjimas:</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="940"/> + <source>Allow</source> + <translation>Leisti</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="941"/> + <source>Deny</source> + <translation>Drausti</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="945"/> + <source>Always</source> + <translation>Visada</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="946"/> + <source>Until reboot</source> + <translation>Iki perkrovimo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> + <source> You are about to delete this rule. </source> + <translation> Ketinate ištrinti šią taisyklę. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> + <source>Process</source> + <translation type="obsolete">Aplicación</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> + <source>Destination</source> + <translation type="obsolete">Destino</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> + <source>Rule</source> + <translation type="obsolete">Regla</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> + <source>UserID</source> + <translation type="obsolete">UserID</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> + <source>LastConnection</source> + <translation type="obsolete">Última Conexión</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> + <source>Name</source> + <comment>xxxxx</comment> + <translation type="obsolete">Nombre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> + <source>Name</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Nombre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Address</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Dirección</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> + <source>Status</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Estado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> + <source>Hostname</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Hostname</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> + <source>Version</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Versión</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> + <source>Rules</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Reglas</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Time</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Hora</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> + <source>Action</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Acción</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> + <source>Duration</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Duración</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> + <source>Node</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Nodo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Enabled</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Habilitado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> + <source>Hits</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Total</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>Protocol</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Protocolo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> + <source>Process</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Aplicación</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> + <source>Destination</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Destino</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> + <source>Rule</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Regla</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> + <source>UserID</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">UserID</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> + <source>LastConnection</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">ÚltimaConexión</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="287"/> + <source>Name</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Pavadinimas</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="288"/> + <source>Address</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Adresas</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="289"/> + <source>Status</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Būsena</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="290"/> + <source>Hostname</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Hostname</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="423"/> + <source>Version</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Versija</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="420"/> + <source>Rules</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Taisyklės</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Time</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Laikas</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> + <source>Action</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Veiksmas</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> + <source>Duration</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Trukmė</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> + <source>Node</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Mazgas</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Enabled</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Įjungta</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="438"/> + <source>Hits</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Hits</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> + <source>Protocol</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Protokolas</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Process</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Procesas</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>Destination</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Paskirties vieta</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> + <source>Rule</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Taisyklė</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> + <source>UserID</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Vartotojo ID</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> + <source>LastConnection</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>PaskutinisPrisijungimas</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> + <source>Args</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="obsolete">Args</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> + <source>DstIP</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>PaskIP</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> + <source>DstHost</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>DstHost</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> + <source>DstPort</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>PaskPrievadas</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="776"/> + <source>New node connected</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="17"/> + <source>What</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="19"/> + <source>Network name</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="291"/> + <source>Uptime</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished">Veikimo laikas</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> + <source>Precedence</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="421"/> + <source>Connections</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished">Connections</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="422"/> + <source>Dropped</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished">Dropped</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="437"/> + <source>What</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="931"/> + <source>Apply to</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="942"/> + <source>Reject</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> + <source>Description</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> + <source>Cmdline</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="406"/> + <source>Export rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="407"/> + <source>Import rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="408"/> + <source>Export events to CSV</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> + <source>Quit</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="932"/> + <source>Export</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="964"/> + <source>To clipboard</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="965"/> + <source>To disk</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2523"/> + <source>Select a directory to export rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1191"/> + <source> Your are about to delete this entry. </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1678"/> + <source> You are about to delete this node. </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1687"/> + <source><b>Error deleting node</b><br><br></source> + <comment>{0}</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2478"/> + <source>Error exporting rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2552"/> + <source>Select a directory with rules to import (JSON files)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2566"/> + <source>Rules imported fine</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="211"/> + <source>WARNING</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="833"/> + <source>Details</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="835"/> + <source>New</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>stats_deleterule</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="774"/> + <source> Your are about to delete this rule. </source> + <translation type="obsolete"> Estás a punto de borrar esta regla. </translation> + </message> +</context> +<context> + <name>stats_deleterule2</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="776"/> + <source> Are you sure?</source> + <translation type="obsolete"> ¿Estás seguro?</translation> + </message> +</context> +<context> + <name>stats_disabled</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="74"/> + <source>Disabled</source> + <translation type="obsolete">Deshabilitado</translation> + </message> +</context> +<context> + <name>stats_notrunning</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="73"/> + <source>Not running</source> + <translation type="obsolete">Parado</translation> + </message> +</context> +<context> + <name>stats_running</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="75"/> + <source>Running</source> + <translation type="obsolete">Interceptando</translation> + </message> +</context> +<context> + <name>stats_wintitle</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> + <source>OpenSnitch Network Statistics</source> + <translation type="obsolete">Eventos de red OpenSnitch</translation> + </message> +</context> +<context> + <name>stats_wintitle2</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="411"/> + <source>OpenSnitch Network Statistics for</source> + <translation type="obsolete">Eventos de OpenSnitch de</translation> + </message> +</context> +</TS> diff --git a/ui/i18n/locales/nb_NO/opensnitch-nb_NO.ts b/ui/i18n/locales/nb_NO/opensnitch-nb_NO.ts new file mode 100644 index 0000000..9773741 --- /dev/null +++ b/ui/i18n/locales/nb_NO/opensnitch-nb_NO.ts @@ -0,0 +1,3236 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="nb_NO"> +<context> + <name>Dialog</name> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="34"/> + <source>opensnitch-qt</source> + <translation>opensnitch-qt</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="300"/> + <source>User ID</source> + <translation>Bruker-ID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="334"/> + <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source> + <translation><html><head/><body><p><span style=" font-weight:600;">Kjørt fra</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="647"/> + <source>TextLabel</source> + <translation>TextLabel</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="437"/> + <source>Source IP</source> + <translation>Kilde-IP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="458"/> + <source>Process ID</source> + <translation>Prosess-ID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="601"/> + <source>Destination IP</source> + <translation>Mål-IP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="622"/> + <source>Dst Port</source> + <translation>Målport</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="702"/> + <source>from this executable</source> + <translation>fra denne kjørbare fil</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="707"/> + <source>from this command line</source> + <translation>fra denne kommandolinje</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="712"/> + <source>this destination port</source> + <translation>denne målport</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="717"/> + <source>this user</source> + <translation>denne bruker</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="722"/> + <source>this destination ip</source> + <translation>denne mål-IP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="751"/> + <source>once</source> + <translation>en gang</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="706"/> + <source>for this session</source> + <translation type="obsolete">durante esta sesión</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="786"/> + <source>forever</source> + <translation>for alltid</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="346"/> + <source>Deny</source> + <translation>Nekt</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="337"/> + <source>Allow</source> + <translation>Tillat</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="865"/> + <source>+</source> + <translation>+</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="781"/> + <source>until reboot</source> + <translation>til omstart</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="727"/> + <source>from this PID</source> + <translation>fra denne PID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="809"/> + <source>action</source> + <translation>handling</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="756"/> + <source>30s</source> + <translation>30s</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="761"/> + <source>5m</source> + <translation>5m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="766"/> + <source>15m</source> + <translation>15m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="771"/> + <source>30m</source> + <translation>30m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="776"/> + <source>1h</source> + <translation>1t</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="14"/> + <source>Firewall</source> + <translation>Brannmur</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="55"/> + <source><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></source> + <translation><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Brannmur</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="320"/> + <source>Inbound</source> + <translation>Inngående</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="313"/> + <source>Outbound</source> + <translation>Utgående</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="275"/> + <source>Profile</source> + <translation>Profil</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="375"/> + <source>Allow inbound connections to a port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="378"/> + <source>Allow service (IN)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="397"/> + <source>Exclude outbound connections to a port from being intercepted</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="406"/> + <source>Allow service (OUT)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="426"/> + <source>New rule</source> + <translation>Ny regel</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="431"/> + <source>Close</source> + <translation>Lukk</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="14"/> + <source>Firewall rule</source> + <translation>Brannmurregel</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="26"/> + <source>Node</source> + <translation>Node</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="38"/> + <source>Enable</source> + <translation type="unfinished">Aktiver</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="50"/> + <source>Description</source> + <translation type="unfinished">Beskrivelse</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="90"/> + <source>Simple</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="154"/> + <source>Add new condition</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="177"/> + <source>Remove selected condition</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="233"/> + <source>Direction</source> + <translation>Retning</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="248"/> + <source>IN</source> + <translation>INN</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="257"/> + <source>OUT</source> + <translation>UT</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="223"/> + <source>Action</source> + <translation>Handling</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="285"/> + <source>ACCEPT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="294"/> + <source>DROP</source> + <translation>DROPP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="303"/> + <source>REJECT</source> + <translation>AVVIS</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="312"/> + <source>RETURN</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="442"/> + <source>Clear</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="453"/> + <source>Delete</source> + <translation>Slett</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="464"/> + <source>Save</source> + <translation type="unfinished">Lagre</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="475"/> + <source>Add</source> + <translation>Legg til</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="266"/> + <source>FORWARD</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="271"/> + <source>PREROUTING</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="276"/> + <source>POSTROUTING</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="321"/> + <source>QUEUE</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="330"/> + <source>DNAT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="335"/> + <source>SNAT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="340"/> + <source>REDIRECT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="359"/> + <source>depending on the Action (i.e.: target), the syntaxis of the parameters will vary. +Some examples: + +QUEUE -> num 0 (or 1, 2, ...) +REDIRECT, TPROXY, DNAT, SNAT, MASQUERADE: + to :22 + to 192.168.1.254:8080 + to 192.168.1.254 + to 1024-2048 (masquerade)</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>PreferencesDialog</name> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="14"/> + <source>Preferences</source> + <translation>Innstillinger</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="484"/> + <source>UI</source> + <translation>Grensesnitt</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="54"/> + <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p></body></html></source> + <translation type="obsolete">Este timeout es la cuenta atrás que aparece cuando se muestra una ventana emergente</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="466"/> + <source>Default timeout</source> + <translation>Forvalgt utløpstid</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="340"/> + <source>Pop-up default duration</source> + <translation>Forvalgt varighet for sprettoppvindu</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="866"/> + <source>Default duration</source> + <translation>Forvalgt varighet</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="162"/> + <source>Pop-up default action</source> + <translation type="obsolete">Acción por defecto de la ventana emergente</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="483"/> + <source>Default action</source> + <translation type="obsolete">Acción por defecto</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="323"/> + <source>Default target</source> + <translation>Forvalgt mål</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="179"/> + <source>center</source> + <translation>midtstilt</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="184"/> + <source>top right</source> + <translation>øvre høyre</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="189"/> + <source>bottom right</source> + <translation>nedre høyre</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="194"/> + <source>top left</source> + <translation>øvre venstre</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="199"/> + <source>bottom left</source> + <translation>nedre venstre</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="167"/> + <source>Prompt dialog default position on screen</source> + <translation type="obsolete">Posición por defecto</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="283"/> + <source>by executable</source> + <translation>etter kjørbar fil</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="288"/> + <source>by command line</source> + <translation>etter kommandolinje</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="293"/> + <source>by destination port</source> + <translation>etter målport</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="298"/> + <source>by destination ip</source> + <translation>etter mål-IP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="303"/> + <source>by user id</source> + <translation>etter bruker-id</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="970"/> + <source>once</source> + <translation>en gang</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="240"/> + <source>for this session</source> + <translation type="obsolete">durante esta sesión</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="249"/> + <source>forever</source> + <translation>for alltid</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1012"/> + <source>deny</source> + <translation>nekt</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1021"/> + <source>allow</source> + <translation>tillat</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="406"/> + <source>Disable pop-ups, only display an alert</source> + <translation type="obsolete">Deshabilitar ventanas emergentes, +sólo mostrar alerta</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="823"/> + <source>Nodes</source> + <translation>Noder</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="829"/> + <source>Process monitor method</source> + <translation>Prosessovervåkingsmetode</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="863"/> + <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source> + <translation><html><head/><body><p>Forvalgt varighet tar effekt når det ikke er noe brukergrensesnitt tilkoblet.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="988"/> + <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source> + <translation><html><head/><body><p>Adressen til noden.</p><p>Forvalgt: unix:///tmp/osui.sock (unix:// er påkrevd hvis det er en Unix-socket)</p><p>Det kan også være en IP-adresse med port: 127.0.0.1:50051</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="991"/> + <source>Address</source> + <translation>Adresse</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1131"/> + <source>Default log level</source> + <translation>Forvalgt loggnivå</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1039"/> + <source>Version</source> + <translation>Versjon</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="902"/> + <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source> + <translation><html><head/><body><p>Forvalgt handling når det ikke er noe brukergrensesnitt tilkoblet.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="846"/> + <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source> + <translation><html><head/><body><p>Loggfiler å skrive logger til.<br/></p><p>/dev/stdout skriver logger til standard-ut.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="849"/> + <source>Log file</source> + <translation>Loggfil</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="578"/> + <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons.</p><p>The pop-up dialog will only contain information about the network connection.</p></body></html></source> + <translation type="obsolete">Si marcas esta opción, OpenSnitch te preguntará para Aceptar o Denegar conexiones que no tengan un PID asociado por diferentes razones. + +La ventana emergente sólo contendrá información relativa a la conexión. + +Nota: Estas conexiones no tienen por qué indicar que algo sospechoso está sucediendo. Simplemente +es que no hemos descubierto el PID (por ejemplo conexiones que no se originan en la máquina, o paquetes en mal estado).</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="581"/> + <source>Intercept Unknown Connections</source> + <translation type="obsolete">Interceptar conexiones desconocidas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="921"/> + <source>HostName</source> + <translation>HostName</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1090"/> + <source>unix:///tmp/osui.sock</source> + <translation>unix:///tmp/osui.sock</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="975"/> + <source>until restart</source> + <translation>frem til omstart</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="980"/> + <source>always</source> + <translation>alltid</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1102"/> + <source>/var/log/opensnitchd.log</source> + <translation>/var/log/opensnitchd.log</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1107"/> + <source>/dev/stdout</source> + <translation>/dev/stdout</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="879"/> + <source>Apply configuration to all nodes</source> + <translation>Anvend oppsett for alle noder</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1146"/> + <source>Database</source> + <translation>Database</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1181"/> + <source>In memory</source> + <translation>I minnet</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1186"/> + <source>File</source> + <translation>Fil</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1482"/> + <source>Close</source> + <translation>Lukk</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1493"/> + <source>Apply</source> + <translation>Anvend</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1504"/> + <source>Save</source> + <translation>Lagre</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="244"/> + <source>until reboot</source> + <translation>frem til omstart</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1200"/> + <source>Database type</source> + <translation>Databasetype</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1207"/> + <source>Select</source> + <translation>Velg</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="83"/> + <source>Pop-ups default options</source> + <translation type="obsolete">Opciones por defecto de las ventanas emergentes</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="367"/> + <source>Pop-ups default position on screen</source> + <translation type="obsolete">Posición en pantalla</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="102"/> + <source><html><head/><body><p>The advanced view allows you to apply more filters on a connection</p><p>when a pop-up appears.</p></body></html></source> + <translation type="obsolete">La vista avanzada permite filtrar conexiones por más parámetros</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="359"/> + <source>Show advanced view by default</source> + <translation>Vis avansert fremvisning som forvalg</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="653"/> + <source>Action</source> + <translation>Handling</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="375"/> + <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="343"/> + <source>Duration</source> + <translation>Varighet</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="263"/> + <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="266"/> + <source>Filter connections also by:</source> + <translation>Filtrer forbindelser også etter:</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="362"/> + <source>If checked, this field will be checked when a pop-up is displayed</source> + <translation type="obsolete">Si lo seleccionas, este campo se usará para filtrar las conexiones</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="81"/> + <source>User ID</source> + <translation>Bruker-ID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="97"/> + <source>Destination port</source> + <translation>Målport</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="113"/> + <source>Destination IP</source> + <translation>Mål-IP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="463"/> + <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="356"/> + <source>The advanced view allows you to easily select multiple fields to filter connections</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="110"/> + <source>If checked, this field will be selected when a pop-up is displayed</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="159"/> + <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="905"/> + <source>Default action when the GUI is disconnected</source> + <translation>Forvalgt handling når det grafiske brukergrensesnittet er frakoblet</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1001"/> + <source>Debug invalid connections</source> + <translation>Feilsøk ugyldige forbindelser</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="39"/> + <source>Pop-ups</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="64"/> + <source>Default options</source> + <translation>Forvalgte innstillinger</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="330"/> + <source>Default position on screen</source> + <translation>Forvalgt posisjon på skjermen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="769"/> + <source>any temporary rules</source> + <translation>alle midertidige regler</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="463"/> + <source>Show events columns</source> + <translation type="obsolete">Mostrar columnas de la pestaña Eventos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="589"/> + <source>Time</source> + <translation>Tid</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="669"/> + <source>Destination</source> + <translation>Mål</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="637"/> + <source>Protocol</source> + <translation>Protokoll</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="685"/> + <source>Process</source> + <translation>Prosess</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="605"/> + <source>Rule</source> + <translation>Regel</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="621"/> + <source>Node</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="723"/> + <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using wireguard.</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Si se selecciona opensnitch te preguntará para permitir o denegar conexiones que no tienen un PID asociado. Esto puede pasar por diferentes motivos, principalmente debido a conexiones inválidas.</p><p>La ventana emergente sólo contendrá información de la conexión.</p><p>Hay algunas situaciones en las que estas conexiones son válidas, por ejemplo al establecer un túnel VPN con wireguard.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="577"/> + <source>Events tab columns</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="308"/> + <source>by PID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="473"/> + <source>Disable pop-ups, only display a notification</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="496"/> + <source>Desktop notifications</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="514"/> + <source>Use system notifications</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="530"/> + <source>Use Qt notifications</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="559"/> + <source>Test</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="998"/> + <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1294"/> + <source>minutes</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1326"/> + <source>Minutes between events purges</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1352"/> + <source>days</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1365"/> + <source>Maximum days of events to keep</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="147"/> + <source>reject</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="716"/> + <source>System</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="695"/> + <source>Command line</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="708"/> + <source>Theme</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="219"/> + <source>30s</source> + <translation>30s</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="224"/> + <source>5m</source> + <translation>5m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="229"/> + <source>15m</source> + <translation>15m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="234"/> + <source>30m</source> + <translation>30m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="239"/> + <source>1h</source> + <translation>1t</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="748"/> + <source>Rules</source> + <translation>Regler</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="756"/> + <source>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI. + +Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="761"/> + <source>Don't save/Delete rules of duration</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="779"/> + <source>30s or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="784"/> + <source>5m or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="789"/> + <source>15m or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="794"/> + <source>30m or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="799"/> + <source>1h or less</source> + <translation>1t eller mindre</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="724"/> + <source>Language</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>ProcessDetailsDialog</name> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="14"/> + <source>Process details</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="61"/> + <source>loading...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="81"/> + <source>CWD: loading...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="93"/> + <source>mem stats: loading...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="121"/> + <source>Status</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="135"/> + <source>Open files</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="149"/> + <source>I/O Statistics</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="163"/> + <source>Memory mapped files</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="177"/> + <source>Stack</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="191"/> + <source>Environment variables</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="210"/> + <source>Application pids</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="240"/> + <source>Start or stop monitoring this process</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="256"/> + <source>Close</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>RulesDialog</name> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="20"/> + <source>Rule</source> + <translation>Regel</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> + <source>Node</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> + <source>Apply rule to all nodes</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="379"/> + <source>From this command line</source> + <translation>Fra denne kommandolinjen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="472"/> + <source>From this executable</source> + <translation>Fra denne kjørbare filen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="56"/> + <source>Action</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="138"/> + <source>/path/to/executable, .*/bin/executable[0-9\.]+$, ...</source> + <translation type="obsolete">/ruta/al/ejecutable, .*/bin/executable[0-9\.]+$, ...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="610"/> + <source>To this IP / Network</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="97"/> + <source>once</source> + <translation type="unfinished">en gang</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="230"/> + <source>until restart</source> + <translation type="obsolete">hasta reiniciar (el servicio)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="132"/> + <source>always</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="902"/> + <source>To this port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="372"/> + <source>From this user ID</source> + <translation>Fra denne bruker-ID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/> + <source>Commas or spaces are not allowed to specify multiple domains. + +Use regular expressions instead: +.*(opensnitch|duckduckgo).com +.*\.google.com + +or a single domain: +www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... +gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="603"/> + <source>www.domain.org, .*\.domain.org</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="526"/> + <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="532"/> + <source>TCP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="760"/> + <source>You can specify a single IP: +- 192.168.1.1 + +or a regular expression: +- 192\.168\.1\.[0-9]+ + +multiple IPs: +- ^(192\.168\.1\.1|172\.16\.0\.1)$ + +You can also specify a subnet: +- 192.168.1.0/24 + +Note: Commas or spaces are not allowed to separate IPs or networks.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="89"/> + <source>Duration</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="633"/> + <source>Protocol</source> + <translation>Protokoll</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="750"/> + <source>To this host</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="151"/> + <source>Deny</source> + <translation>Nekt</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="191"/> + <source>Allow</source> + <translation>Tillat</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="207"/> + <source>Enable</source> + <translation type="unfinished">Aktiver</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="238"/> + <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them. + +000-allow-localhost +001-deny-broadcast +...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="214"/> + <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one. + +You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example: + +[x] Priority - 000-priority-rule +[ ] Priority - 001-less-priority-rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="222"/> + <source>Priority rule</source> + <translation>Prioritetsregel</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1092"/> + <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1095"/> + <source>Case-sensitive</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="127"/> + <source>until reboot</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="980"/> + <source>To this list of domains</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="539"/> + <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Selecciona un directorio con listas de dominios para permitir o denegar.</p><p>Mete dentro de este directorio ficheros con cualquier extensión que contengan listas de dominios.</p><p><br/>El formato de cada dominio de la lista tiene que estar en formato hosts, así:</p><p>127.0.0.1 www.domain.com</p><p>o </p><p>0.0.0.0 www.domain.com</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="346"/> + <source>Applications</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="216"/> + <source><html><head/><body><p>This field will only match the executable path. It is not modifiable by the user.<br/></p><p>You can use regular expressions to deny executions from /tmp for example:<br/></p><p>^/tmp/.*$</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Este campo sólo comprueba la ruta del ejecutable (la cual no es modificable por el usuario).<br/></p><p>Puedes usar expresiones regulares para denegar cualquier ejecución desde /tmp, por ejemplo; ^/tmp/.*$</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="389"/> + <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="399"/> + <source>From this PID</source> + <translation>Fra denne PID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="491"/> + <source>Network</source> + <translation>Nettverk</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="932"/> + <source>List of domains/IPs</source> + <translation>Liste med domener/IP-nummer</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="938"/> + <source>To this list of network ranges</source> + <translation>Til denne listen med nettverkområder</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="945"/> + <source>To this list of IPs</source> + <translation>Til denne listen med IP-adresser</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="971"/> + <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1006"/> + <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1034"/> + <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1049"/> + <source>To this list of domains +(regular expressions)</source> + <translation>Til denne listen med domener +(regulæruttrykk)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1076"/> + <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="168"/> + <source>Reject</source> + <translation>Avvis</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1151"/> + <source>Description...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="355"/> + <source><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="365"/> + <source>Is regular expression</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> + <source>is regular expression</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="863"/> + <source>Network interface</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1086"/> + <source>More</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1102"/> + <source>Don't log connections that match this rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1105"/> + <source>Don't log connections</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="148"/> + <source>Deny will just discard the connection</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="165"/> + <source>Reject will drop the connection, and kill the socket that initiated it</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="185"/> + <source>Allow will allow the connection</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="566"/> + <source>ICMP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="571"/> + <source>ICMP6</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="576"/> + <source>SCTP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="581"/> + <source>SCTP6</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="937"/> + <source>Color</source> + <translation type="obsolete">Farge</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="245"/> + <source>Name</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="743"/> + <source>From this IP / Network</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="872"/> + <source>From this port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="918"/> + <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>StatsDialog</name> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="34"/> + <source>OpenSnitch Network Statistics</source> + <translation>OpenSnitch nettverkstatistikk</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="287"/> + <source>Save to CSV</source> + <translation type="obsolete">Lagre til CSV.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="297"/> + <source>Ctrl+S</source> + <translation type="obsolete">Ctrl+S</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="333"/> + <source>Create a new rule</source> + <translation>Lag ny regel</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="376"/> + <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source> + <translation><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">vertsnavn - 192.168.1.1</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="413"/> + <source>Status</source> + <translation>Status</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1793"/> + <source>-</source> + <translation>-</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="451"/> + <source>Start or Stop interception</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="496"/> + <source>Events</source> + <translation>Hendelser</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="94"/> + <source>Filter</source> + <translation>Filter</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="107"/> + <source>Allow</source> + <translation>Tillat</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="116"/> + <source>Deny</source> + <translation>Nekt</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="143"/> + <source>Ex.: firefox</source> + <translation>F.eks.: firefox</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="205"/> + <source>50</source> + <translation>50</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="210"/> + <source>100</source> + <translation>100</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="215"/> + <source>200</source> + <translation>200</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="220"/> + <source>300</source> + <translation>300</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="826"/> + <source>Nodes</source> + <translation>Noder</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="554"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Addr column to view details of a node)</span></p></body></html></source> + <translation type="obsolete">(doble click en la columna Dirección para ver los detalles)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1700"/> + <source>Rules</source> + <translation>Regler</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="995"/> + <source>enable</source> + <translation>aktiver</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="684"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Name column to view details of a rule)</span></p></body></html></source> + <translation type="obsolete">(doble click en la columna Nombre para ver los detalles)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="692"/> + <source>search rule name</source> + <translation type="obsolete">buscar regla</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="782"/> + <source>Application rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="936"/> + <source>Permanent</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="945"/> + <source>Temporary</source> + <translation>Midlertidig</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1063"/> + <source>Hosts</source> + <translation>Verter</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1364"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click to view details of an item)</span></p></body></html></source> + <translation type="obsolete">(doble click en un dominio para ver detalles)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1153"/> + <source>Applications</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1266"/> + <source>Addresses</source> + <translation>Adresser</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1356"/> + <source>Ports</source> + <translation>Porter</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1440"/> + <source>Users</source> + <translation>Brukere</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1544"/> + <source>Connections</source> + <translation>Forbindelser</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1596"/> + <source>Dropped</source> + <translation>Droppet</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1648"/> + <source>Uptime</source> + <translation>Oppetid</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1767"/> + <source>Version</source> + <translation>Versjon</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="233"/> + <source>Delete all intercepted events</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1022"/> + <source>Edit rule</source> + <translation>Endre regel</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1039"/> + <source>Delete rule</source> + <translation>Slett regel</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="926"/> + <source>Delete all intercepted hosts</source> + <translation type="obsolete">Borrar todos los hosts</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1051"/> + <source>Delete all intercepted applications</source> + <translation type="obsolete">Borrar todos las aplicaciones</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1159"/> + <source>Delete all intercepted addresses</source> + <translation type="obsolete">Borrar todas las direcciones</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1261"/> + <source>Delete all intercepted ports</source> + <translation type="obsolete">Borrar todos los puertos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1371"/> + <source>Delete all intercepted users</source> + <translation type="obsolete">Borrar todos los usuarios</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="699"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click on a row to view details of a rule)</span></p></body></html></source> + <translation type="obsolete">(Doble click en una fila para editar una regla)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="912"/> + <source>Delete connections that matched this rule</source> + <translation type="obsolete">Borrar conexiones que coinciden con esta regla</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="927"/> + <source>All applications</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="125"/> + <source>Reject</source> + <translation>Avvis</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="180"/> + <source>0</source> + <translation>0</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="777"/> + <source>2</source> + <translation type="unfinished">2</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="954"/> + <source>System rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="637"/> + <source>Delete this node</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="653"/> + <source>Show the preferences of this node</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="669"/> + <source>Start or stop interception of this node</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>contextual_menu</name> + <message> + <location filename="../../../opensnitch/service.py" line="47"/> + <source>Statistics</source> + <translation>Statistikk</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="50"/> + <source>Help</source> + <translation>Hjelp</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="51"/> + <source>Close</source> + <translation>Lukk</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="48"/> + <source>Enable</source> + <translation>Aktiver</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="49"/> + <source>Disable</source> + <translation>Deaktiver</translation> + </message> +</context> +<context> + <name>firewall</name> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="91"/> + <source>Configuration applied.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="404"/> + <source>Error: {0}</source> + <translation>Feil: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="193"/> + <source>Applying changes...</source> + <translation>Tar i bruk endringer...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="230"/> + <source>Error getting INPUT chain policy</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="237"/> + <source>Error getting OUTPUT chain policy</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="290"/> + <source>In order to configure firewall rules from the GUI, we need to use 'nftables' instead of 'iptables'</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="304"/> + <source>Enabling firewall...</source> + <translation>Aktiverer brannmur...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="306"/> + <source>Disabling firewall...</source> + <translation>Deaktiverer brannmur...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="71"/> + <source>Dest Port</source> + <translation>Målport</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="72"/> + <source>Source Port</source> + <translation>Kildeport</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="73"/> + <source>Dest IP</source> + <translation>Mål-IP</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="74"/> + <source>Source IP</source> + <translation type="unfinished">Kilde-IP</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="75"/> + <source>Input interface</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="76"/> + <source>Output interface</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="77"/> + <source>Set conntrack mark</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="78"/> + <source>Match conntrack mark</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="79"/> + <source>Match conntrack state(s)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="80"/> + <source>Set mark on packet</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="81"/> + <source>Match packet information</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="87"/> + <source>Bandwidth quotas</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="89"/> + <source>Rate limit connections</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="373"/> + <source>Your protobuf version is incompatible, you need to install protobuf 3.8.0 or superior +(pip3 install --ignore-installed protobuf==3.8.0)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="397"/> + <source>Rule deleted</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="401"/> + <source>Rule added</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="420"/> + <source>You can use ',' or '-' to specify multiple ports/IPs or ranges/values:<br><br>ports: 22 or 22,443 or 50000-60000<br>IPs: 192.168.1.1 or 192.168.1.30-192.168.1.130<br>Values: echo-reply,echo-request<br>Values: new,established,related</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="440"/> + <source>Deleting rule, wait</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="443"/> + <source>Error updating rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="483"/> + <source>Adding rule, wait</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="492"/> + <source><select a statement></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="787"/> + <source>Equal</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="788"/> + <source>Not equal</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="789"/> + <source>Greater or equal than</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="790"/> + <source>Greater than</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="791"/> + <source>Less or equal than</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="792"/> + <source>Less than</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1350"/> + <source>Firewall rule</source> + <translation>Brannmurregel</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="885"/> + <source>Simple</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="890"/> + <source>Advanced</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1046"/> + <source>This rule is not supported yet.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1111"/> + <source>Exclude service</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1123"/> + <source>Allow inbound connections to the selected port.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1125"/> + <source>Allow outbound connections to the selected port.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1201"/> + <source>select a statement.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1217"/> + <source>value cannot be 0 or empty.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1229"/> + <source>the value format is 1024/kbytes (or bytes, mbytes, gbytes)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1240"/> + <source>the value format is 1024/kbytes/second (or bytes, mbytes, gbytes)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1243"/> + <source>rate-limit not valid, use: bytes, kbytes, mbytes or gbytes.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1245"/> + <source>time-limit not valid, use: second, minute, hour or day</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1293"/> + <source>port not valid.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="108"/> + <source> +Supported formats: + + - Simple: 23 + - Ranges: 80-1024 + - Multiple ports: 80,443,8080 +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="134"/> + <source> +Supported formats: + + - Simple: 1.2.3.4 + - IP ranges: 1.2.3.100-1.2.3.200 + - Network ranges: 1.2.3.4/24 +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="147"/> + <source>Match input interface. Regular expressions not allowed.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="154"/> + <source>Match output interface. Regular expressions not allowed.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="161"/> + <source>Set a conntrack mark on the connection, in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="171"/> + <source>Match a conntrack mark of the connection, in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="178"/> + <source>Match conntrack states. + +Supported formats: + - Simple: new + - Multiple states separated by commas: related,new +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="193"/> + <source> +Match packet's metainformation. + +Value must be in decimal format, except for the "l4proto" option. +For l4proto it can be a lower case string, for example: + tcp + udp + icmp, + etc + +If the value is decimal for protocol or lproto, it'll use it as the code of +that protocol. +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="213"/> + <source>Set a mark on the packet matching the specified conditions. The value is in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="221"/> + <source> +Match ICMP codes. + +Supported formats: + - Simple: echo-request + - Multiple separated by commas: echo-request,echo-reply +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="234"/> + <source> +Match ICMPv6 codes. + +Supported formats: + - Simple: echo-request + - Multiple separated by commas: echo-request,echo-reply +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="247"/> + <source>Print a message when this rule matches a packet.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="254"/> + <source> +Apply quotas on connections. + +For example when: + - "quota over 10/mbytes" -> apply the Action defined (DROP) + - "quota until 10/mbytes" -> apply the Action defined (ACCEPT) + +The value must be in the format: VALUE/UNITS, for example: + - 10mbytes, 1/gbytes, etc +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="286"/> + <source> +Apply limits on connections. + +For example when: + - "limit over 10/mbytes/minute" -> apply the Action defined (DROP, ACCEPT, etc) + (When there're more than 10MB per minute, apply an Action) + + - "limit until 10/mbytes/hour" -> apply the Action defined (ACCEPT) + +The value must be in the format: VALUE/UNITS/TIME, for example: + - 10/mbytes/minute, 1/gbytes/hour, etc +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="607"/> + <source>num</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="621"/> + <source>to</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>menu_close</name> + <message> + <location filename="../../../opensnitch/service.py" line="131"/> + <source>Close</source> + <translation type="obsolete">Cerrar</translation> + </message> +</context> +<context> + <name>menu_help</name> + <message> + <location filename="../../../opensnitch/service.py" line="126"/> + <source>Help</source> + <translation type="obsolete">Ayuda</translation> + </message> +</context> +<context> + <name>menu_statistics</name> + <message> + <location filename="../../../opensnitch/service.py" line="120"/> + <source>Statistics</source> + <translation type="obsolete">Eventos</translation> + </message> +</context> +<context> + <name>messages</name> + <message> + <location filename="../../../opensnitch/service.py" line="281"/> + <source>Info</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="285"/> + <source>Error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="289"/> + <source>Warning</source> + <translation type="unfinished">Aviso</translation> + </message> +</context> +<context> + <name>notifications</name> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="654"/> + <source>System notifications are not available, you need to install python3-notify2.</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>popups</name> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="115"/> + <source>Allow</source> + <translation>Tillat</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="114"/> + <source>Deny</source> + <translation>Nekt</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/> + <source>forever</source> + <translation>for alltid</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="331"/> + <source>Outgoing connection</source> + <translation>Utgående forbindelse</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="336"/> + <source>Process launched from:</source> + <translation>Prosess startet fra:</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="377"/> + <source>from this command line</source> + <translation>fra denne kommandolinjen</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="373"/> + <source>from this executable</source> + <translation type="unfinished">fra denne kjørbare fil</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="208"/> + <source>Unknown process</source> + <translation type="obsolete">Proceso no encontrado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="50"/> + <source>until reboot</source> + <translation>frem til omstart</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="379"/> + <source>to port {0}</source> + <translation>til port {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="222"/> + <source><b>%s</b> is connecting to <b>%s</b> on %s port %d</source> + <translation type="obsolete"><b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="228"/> + <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source> + <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="442"/> + <source>to {0}</source> + <translation>til {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="382"/> + <source>from user {0}</source> + <translation>fra bruker {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="399"/> + <source>to {0}.*</source> + <translation>til {0}.*</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="452"/> + <source>to *.{0}</source> + <translation>til *.{0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="378"/> + <source>to *{0}</source> + <translation type="obsolete">a *{0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="486"/> + <source><b>Remote</b> process %s running on <b>%s</b></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="490"/> + <source>is connecting to <b>%s</b> on %s port %d</source> + <translation>oppretter forbindelse til <b>%s</b> på %s port %d</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="502"/> + <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="386"/> + <source>from this PID</source> + <translation type="unfinished">fra denne PID</translation> + </message> + <message> + <location filename="../../../opensnitch/notifications.py" line="122"/> + <source>New outgoing connection</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="116"/> + <source>Reject</source> + <translation type="unfinished">Rechazar</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="497"/> + <source>is connecting to <b>%s</b>, %s</source> + <translation>oppretter forbindelse til <b>%s</b>, %s</translation> + </message> + <message> + <location filename="../../../opensnitch/notifications.py" line="42"/> + <source>Open</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>popups2</name> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="254"/> + <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source> + <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> + </message> +</context> +<context> + <name>preferences</name> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="171"/> + <source>Exception saving config: %s</source> + <translation type="obsolete">Error al guarda la configuración: %s</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="177"/> + <source>Applying configuration on %s ...</source> + <translation type="obsolete">Aplicando configuración en %s ...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="292"/> + <source>Server address can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="227"/> + <source>Error loading %s configuration</source> + <translation type="obsolete">Error al cargar la configuración %s</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="568"/> + <source>Configuration applied.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="257"/> + <source>Error applying configuration: %s</source> + <translation type="obsolete">Error al aplicar la configuración: %s</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="386"/> + <source>Exception saving config: {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="500"/> + <source>Applying configuration on {0} ...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="321"/> + <source>Error loading {0} configuration</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="570"/> + <source>Error applying configuration: {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> + <source>Warning</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> + <source>You must select a file for the database<br>or choose "In memory" type.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="401"/> + <source>DB type changed</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="38"/> + <source>Restart the GUI in order effects to take effect</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="607"/> + <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="466"/> + <source>System</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="187"/> + <source>Themes not available. Install qt-material: pip3 install qt-material</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> + <source>UI theme changed</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> + <source>Restart the GUI in order to apply the new theme</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="508"/> + <source>Ok</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="388"/> + <source>There're no nodes connected</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="520"/> + <source>Exception saving node config {0}: {1}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="164"/> + <source>System default</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="433"/> + <source>Language changed</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>proc_details</name> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="100"/> + <source><b>Error loading process information:</b> <br><br> + +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="119"/> + <source><b>Error stopping monitoring process:</b><br><br></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="159"/> + <source>loading...</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>rules</name> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="228"/> + <source>There're no nodes connected.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="271"/> + <source>Rule applied.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="123"/> + <source>Error applying rule: %s</source> + <translation type="obsolete">Error al aplicar la regla: %s</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="641"/> + <source>protocol can not be empty, or uncheck it</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="655"/> + <source>Protocol regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="659"/> + <source>process path can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="673"/> + <source>Process path regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="677"/> + <source>command line can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="691"/> + <source>Command line regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="731"/> + <source>Dest port can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="745"/> + <source>Dst port regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="749"/> + <source>Dest host can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="763"/> + <source>Dst host regexp error</source> + <translation>Feil med målvert-regulæruttrykk</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="805"/> + <source>Dest IP/Network can not be empty</source> + <translation>Mål-IP/nettverk kan ikke være blank</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="831"/> + <source>Dst IP regexp error</source> + <translation>Feil med regulæruttrykk for mål-IP</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="843"/> + <source>User ID can not be empty</source> + <translation>Bruker-ID kan ikke være blank</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="857"/> + <source>User ID regexp error</source> + <translation>Feil med regulæruttrykk for bruker-ID</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="273"/> + <source>Error applying rule: {0}</source> + <translation>Feil ved anvending av regel: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="931"/> + <source>Lists field cannot be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="933"/> + <source>Lists field must be a directory</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="976"/> + <source><b>Rule not supported</b></source> + <translation><b>Regelen støttes ikke</b></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="539"/> + <source><b>Error loading rule</b></source> + <translation><b>Feil ved regellasting</b></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="245"/> + <source>There's already a rule with this name.</source> + <translation>Det er allerede en regel med dette navnet.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="861"/> + <source>PID field can not be empty</source> + <translation>PID-feltet kan ikke være blankt</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="875"/> + <source>PID field regexp error</source> + <translation>Feil med regulæruttrykk for PID-felt</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="963"/> + <source>Select at least one field.</source> + <translation>Velg minst ett felt.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="695"/> + <source>Network interface can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="709"/> + <source>Network interface regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="713"/> + <source>Source port can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="727"/> + <source>Source port regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="767"/> + <source>Source IP/Network can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="793"/> + <source>Source IP regexp error</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>stats</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="313"/> + <source>Not running</source> + <translation>Kjører ikke</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="314"/> + <source>Disabled</source> + <translation>Deaktivert</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="315"/> + <source>Running</source> + <translation>Kjører</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="412"/> + <source>OpenSnitch Network Statistics</source> + <translation type="obsolete">Eventos de OpenSnitch</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="414"/> + <source>OpenSnitch Network Statistics for</source> + <translation type="obsolete">Eventos de OpenSnitch de</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1189"/> + <source> Your are about to delete this rule. </source> + <translation> Du er i ferd med å slette denne regelen. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> + <source> Are you sure?</source> + <translation> Er du sikker?</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="636"/> + <source>OpenSnitch Network Statistics {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="638"/> + <source>OpenSnitch Network Statistics for {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> + <source>Name</source> + <translation type="obsolete">Nombre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Address</source> + <translation type="obsolete">Dirección</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="176"/> + <source>Status</source> + <translation type="obsolete">Estado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="177"/> + <source>Hostname</source> + <translation type="obsolete">Hostname</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="183"/> + <source>Version</source> + <translation type="obsolete">Versión</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="834"/> + <source>Rules</source> + <translation type="unfinished">Reglas</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Time</source> + <translation type="obsolete">Hora</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="875"/> + <source>Action</source> + <translation type="unfinished">Acción</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> + <source>Duration</source> + <translation type="obsolete">Duración</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> + <source>Node</source> + <translation type="obsolete">Nodo</translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="18"/> + <source>Hits</source> + <translation>Treff</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>Protocol</source> + <translation type="obsolete">Protocolo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2581"/> + <source>Save as CSV</source> + <translation>Lagre som CSV</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Enabled</source> + <translation type="obsolete">Habilitado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="961"/> + <source>Delete</source> + <translation>Slett</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="948"/> + <source>always</source> + <translation type="obsolete">siempre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="580"/> + <source><b>Error:</b><br><br>{0}</source> + <translation type="obsolete"><b>Error:</b><br><br>{0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="954"/> + <source>Disable</source> + <translation>Deaktiver</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="956"/> + <source>Enable</source> + <translation>Aktiver</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="959"/> + <source>Duplicate</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="960"/> + <source>Edit</source> + <translation>Rediger</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1248"/> + <source>Rule not found by that name and node</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1301"/> + <source><b>Error:</b><br><br></source> + <comment>{0}</comment> + <translation><b>Feil:</b><br><br></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1308"/> + <source>Warning:</source> + <translation>Advarsel:</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="940"/> + <source>Allow</source> + <translation>Tillat</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="941"/> + <source>Deny</source> + <translation type="unfinished">Nekt</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="945"/> + <source>Always</source> + <translation>Alltid</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="946"/> + <source>Until reboot</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> + <source> You are about to delete this rule. </source> + <translation> Du er i ferd med å slette denne regelen. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> + <source>Process</source> + <translation type="obsolete">Aplicación</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> + <source>Destination</source> + <translation type="obsolete">Destino</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> + <source>Rule</source> + <translation type="obsolete">Regla</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> + <source>UserID</source> + <translation type="obsolete">UserID</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="174"/> + <source>LastConnection</source> + <translation type="obsolete">ÚltimaConexión</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> + <source>Name</source> + <comment>xxxxx</comment> + <translation type="obsolete">Nombre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> + <source>Name</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Nombre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Address</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Dirección</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> + <source>Status</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Estado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> + <source>Hostname</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Hostname</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> + <source>Version</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Versión</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> + <source>Rules</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Reglas</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Time</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Hora</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> + <source>Action</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Acción</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> + <source>Duration</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Duración</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> + <source>Node</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Nodo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Enabled</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Habilitado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> + <source>Hits</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Total</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>Protocol</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Protocolo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> + <source>Process</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Aplicación</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> + <source>Destination</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Destino</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> + <source>Rule</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Regla</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> + <source>UserID</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">UserID</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> + <source>LastConnection</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">ÚltimaConexión</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="287"/> + <source>Name</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Navn</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="288"/> + <source>Address</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Adresse</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="289"/> + <source>Status</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Status</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="290"/> + <source>Hostname</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Vertsnavn</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="423"/> + <source>Version</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Versjon</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="420"/> + <source>Rules</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Regler</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Time</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Tid</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> + <source>Action</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Handling</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> + <source>Duration</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Varighet</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> + <source>Node</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Enabled</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Aktivert</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="438"/> + <source>Hits</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Treff</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> + <source>Protocol</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Protokoll</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Process</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Prosess</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>Destination</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Mål</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> + <source>Rule</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Regel</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> + <source>UserID</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>BrukerID</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> + <source>LastConnection</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>SisteForbindelse</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> + <source>Args</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="obsolete">Args</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> + <source>DstIP</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>MålIP</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> + <source>DstHost</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>MålVert</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> + <source>DstPort</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>MålPort</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="175"/> + <source>Addr</source> + <translation type="obsolete">Dirección</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="181"/> + <source>Connections</source> + <translation type="obsolete">Conexiones</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="182"/> + <source>Dropped</source> + <translation type="obsolete">Rechazadas</translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="17"/> + <source>What</source> + <translation>Hva</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="931"/> + <source>Apply to</source> + <translation>Anvend på</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="942"/> + <source>Reject</source> + <translation>Avvis</translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="19"/> + <source>Network name</source> + <translation>Nettverknavn</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="378"/> + <source>Addr</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="obsolete">Dirección</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="291"/> + <source>Uptime</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Oppetid</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="421"/> + <source>Connections</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Forbindelser</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="422"/> + <source>Dropped</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Droppet</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="437"/> + <source>What</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Hva</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> + <source>Precedence</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Prioritet</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="776"/> + <source>New node connected</source> + <translation>Koblet opp ny node</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> + <source>Description</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Beskrivelse</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> + <source>Cmdline</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Kommandolinje</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="406"/> + <source>Export rules</source> + <translation>Eksporter regler</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="407"/> + <source>Import rules</source> + <translation>Importer regler</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="408"/> + <source>Export events to CSV</source> + <translation>Eksporter hendelser til CSV</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> + <source>Quit</source> + <translation>Avslutt</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="932"/> + <source>Export</source> + <translation>Eksporter</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="964"/> + <source>To clipboard</source> + <translation>Til utklippstavle</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="965"/> + <source>To disk</source> + <translation>Til disk</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2523"/> + <source>Select a directory to export rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1191"/> + <source> Your are about to delete this entry. </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1678"/> + <source> You are about to delete this node. </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1687"/> + <source><b>Error deleting node</b><br><br></source> + <comment>{0}</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2478"/> + <source>Error exporting rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2552"/> + <source>Select a directory with rules to import (JSON files)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2566"/> + <source>Rules imported fine</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="211"/> + <source>WARNING</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="833"/> + <source>Details</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="835"/> + <source>New</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>stats_deleterule</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="774"/> + <source> Your are about to delete this rule. </source> + <translation type="obsolete"> Estás a punto de borrar esta regla. </translation> + </message> +</context> +<context> + <name>stats_deleterule2</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="776"/> + <source> Are you sure?</source> + <translation type="obsolete"> ¿Estás seguro?</translation> + </message> +</context> +<context> + <name>stats_disabled</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="74"/> + <source>Disabled</source> + <translation type="obsolete">Deshabilitado</translation> + </message> +</context> +<context> + <name>stats_notrunning</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="73"/> + <source>Not running</source> + <translation type="obsolete">Parado</translation> + </message> +</context> +<context> + <name>stats_running</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="75"/> + <source>Running</source> + <translation type="obsolete">Interceptando</translation> + </message> +</context> +<context> + <name>stats_wintitle</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> + <source>OpenSnitch Network Statistics</source> + <translation type="obsolete">Eventos de red OpenSnitch</translation> + </message> +</context> +<context> + <name>stats_wintitle2</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="411"/> + <source>OpenSnitch Network Statistics for</source> + <translation type="obsolete">Eventos de OpenSnitch de</translation> + </message> +</context> +</TS> diff --git a/ui/i18n/locales/nl_NL/opensnitch-nl_NL.ts b/ui/i18n/locales/nl_NL/opensnitch-nl_NL.ts new file mode 100644 index 0000000..b0175ad --- /dev/null +++ b/ui/i18n/locales/nl_NL/opensnitch-nl_NL.ts @@ -0,0 +1,3310 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="nl"> +<context> + <name>Dialog</name> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="34"/> + <source>opensnitch-qt</source> + <translation>opensnitch-qt</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="300"/> + <source>User ID</source> + <translation>Gebruikers-id</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="334"/> + <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source> + <translation><html><head/><body><p><span style=" font-weight:600;">Wordt uitgevoerd op</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="647"/> + <source>TextLabel</source> + <translation>TekstLabel</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="437"/> + <source>Source IP</source> + <translation>Bron-ip</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="458"/> + <source>Process ID</source> + <translation>Proces-id</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="601"/> + <source>Destination IP</source> + <translation>Bestemmings-ip</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="622"/> + <source>Dst Port</source> + <translation>Bestemmingspoort</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="702"/> + <source>from this executable</source> + <translation>dit uitvoerbare bestand</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="707"/> + <source>from this command line</source> + <translation>deze opdrachtregel</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="712"/> + <source>this destination port</source> + <translation>deze bestemmingspoort</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="717"/> + <source>this user</source> + <translation>deze gebruiker</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="722"/> + <source>this destination ip</source> + <translation>deze bestemmings-ip</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="751"/> + <source>once</source> + <translation>eenmalig</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="706"/> + <source>for this session</source> + <translation type="obsolete">durante esta sesión</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="786"/> + <source>forever</source> + <translation>oneindig</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="346"/> + <source>Deny</source> + <translation>Weigeren</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="337"/> + <source>Allow</source> + <translation>Toestaan</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="865"/> + <source>+</source> + <translation>+</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="781"/> + <source>until reboot</source> + <translation>tot herstart</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="727"/> + <source>from this PID</source> + <translation>deze PID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="809"/> + <source>action</source> + <translation>actie</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="756"/> + <source>30s</source> + <translation>30 sec.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="761"/> + <source>5m</source> + <translation>5 min.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="766"/> + <source>15m</source> + <translation>15 min.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="771"/> + <source>30m</source> + <translation>30 min.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="776"/> + <source>1h</source> + <translation>1 uur</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="14"/> + <source>Firewall</source> + <translation>Firewall</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="55"/> + <source><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></source> + <translation><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="320"/> + <source>Inbound</source> + <translation>Inkomend</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="313"/> + <source>Outbound</source> + <translation>Uitgaand</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="275"/> + <source>Profile</source> + <translation>Profiel</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="375"/> + <source>Allow inbound connections to a port</source> + <translation>Sta inkomende verbindingen op een bepaalde poort toe</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="378"/> + <source>Allow service (IN)</source> + <translation>Dienst toestaan (IN)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="397"/> + <source>Exclude outbound connections to a port from being intercepted</source> + <translation>Weiger uitgaande verbindingen op een bepaalde poort</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="406"/> + <source>Allow service (OUT)</source> + <translation>Dienst toestaan (UIT)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="426"/> + <source>New rule</source> + <translation>Nieuwe regel</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="453"/> + <source>xxx</source> + <translation type="obsolete">xxx</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="431"/> + <source>Close</source> + <translation>Sluiten</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="14"/> + <source>Firewall rule</source> + <translation>Firewallregel</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="26"/> + <source>Node</source> + <translation>Knooppunt</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="34"/> + <source>All</source> + <translation type="obsolete">Alles</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="38"/> + <source>Enable</source> + <translation>Inschakelen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="50"/> + <source>Description</source> + <translation>Beschrijving</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="90"/> + <source>Simple</source> + <translation>Eenvoudig</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="154"/> + <source>Add new condition</source> + <translation>Voorwaarde toevoegen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="177"/> + <source>Remove selected condition</source> + <translation>Voorwaarde verwijderen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="233"/> + <source>Direction</source> + <translation>Richting</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="248"/> + <source>IN</source> + <translation>IN</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="257"/> + <source>OUT</source> + <translation>UIT</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="223"/> + <source>Action</source> + <translation>Actie</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="285"/> + <source>ACCEPT</source> + <translation>TOESTAAN</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="294"/> + <source>DROP</source> + <translation>AFWIJZEN</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="303"/> + <source>REJECT</source> + <translation>WEIGEREN</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="312"/> + <source>RETURN</source> + <translation>TERUGSTUREN</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="442"/> + <source>Clear</source> + <translation>Wissen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="453"/> + <source>Delete</source> + <translation>Verwijderen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="464"/> + <source>Save</source> + <translation>Opslaan</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="475"/> + <source>Add</source> + <translation>Toevoegen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="266"/> + <source>FORWARD</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="271"/> + <source>PREROUTING</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="276"/> + <source>POSTROUTING</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="321"/> + <source>QUEUE</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="330"/> + <source>DNAT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="335"/> + <source>SNAT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="340"/> + <source>REDIRECT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="359"/> + <source>depending on the Action (i.e.: target), the syntaxis of the parameters will vary. +Some examples: + +QUEUE -> num 0 (or 1, 2, ...) +REDIRECT, TPROXY, DNAT, SNAT, MASQUERADE: + to :22 + to 192.168.1.254:8080 + to 192.168.1.254 + to 1024-2048 (masquerade)</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>PreferencesDialog</name> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="14"/> + <source>Preferences</source> + <translation>Instellingen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="484"/> + <source>UI</source> + <translation>Vormgeving</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="54"/> + <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p></body></html></source> + <translation type="obsolete">Este timeout es la cuenta atrás que aparece cuando se muestra una ventana emergente</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="466"/> + <source>Default timeout</source> + <translation>Stadaard time-out</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="340"/> + <source>Pop-up default duration</source> + <translation>De standaard meldingsduur</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="866"/> + <source>Default duration</source> + <translation>Standaardduur</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="162"/> + <source>Pop-up default action</source> + <translation type="obsolete">Acción por defecto de la ventana emergente</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="483"/> + <source>Default action</source> + <translation type="obsolete">Acción por defecto</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="323"/> + <source>Default target</source> + <translation>Standaarddoel</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="179"/> + <source>center</source> + <translation>Midden</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="184"/> + <source>top right</source> + <translation>Rechtsboven</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="189"/> + <source>bottom right</source> + <translation>Rechtsonder</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="194"/> + <source>top left</source> + <translation>Linksboven</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="199"/> + <source>bottom left</source> + <translation>Linksonder</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="167"/> + <source>Prompt dialog default position on screen</source> + <translation type="obsolete">Posición por defecto</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="283"/> + <source>by executable</source> + <translation>Uitvoerbaar bestand</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="288"/> + <source>by command line</source> + <translation>Opdrachtregel</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="293"/> + <source>by destination port</source> + <translation>Bestemmingspoort</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="298"/> + <source>by destination ip</source> + <translation>Bestemmings-ip</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="303"/> + <source>by user id</source> + <translation>Gebruikers-id</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="970"/> + <source>once</source> + <translation>Eenmalig</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="240"/> + <source>for this session</source> + <translation type="obsolete">durante esta sesión</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="249"/> + <source>forever</source> + <translation>Oneindig</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1012"/> + <source>deny</source> + <translation>Weigeren</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1021"/> + <source>allow</source> + <translation>Toestaan</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="406"/> + <source>Disable pop-ups, only display an alert</source> + <translation type="obsolete">Deshabilitar ventanas emergentes, +sólo mostrar alerta</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="823"/> + <source>Nodes</source> + <translation>Knooppunten</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="829"/> + <source>Process monitor method</source> + <translation>Procesmonitormethode</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="863"/> + <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source> + <translation><html><head/><body><p>De standaardduur die wordt gebruikt als er geen grafisch programma is.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="988"/> + <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source> + <translation><html><head/><body><p>Het adres van het knooppunt.</p><p>Standaard: unix:///tmp/osui.sock (unix:// is vereist bij gebruik van een Unix-socket)</p><p>Dit kan ook een ip-adres met poort zijn: 127.0.0.1:50051</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="991"/> + <source>Address</source> + <translation>Adres</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1131"/> + <source>Default log level</source> + <translation>Standaard logniveau</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1039"/> + <source>Version</source> + <translation>Versie</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="902"/> + <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source> + <translation><html><head/><body><p>De standaardactie die wordt uitgevoerd als er geen grafisch programma is.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="846"/> + <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source> + <translation><html><head/><body><p>Het logboek waarin logregels worden genoteerd.<br/></p><p>/dev/stdout voegt regels toe aan de standaarduitvoer.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="849"/> + <source>Log file</source> + <translation>Logboek</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="578"/> + <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons.</p><p>The pop-up dialog will only contain information about the network connection.</p></body></html></source> + <translation type="obsolete">Si marcas esta opción, OpenSnitch te preguntará para Aceptar o Denegar conexiones que no tengan un PID asociado por diferentes razones. + +La ventana emergente sólo contendrá información relativa a la conexión. + +Nota: Estas conexiones no tienen por qué indicar que algo sospechoso está sucediendo. Simplemente +es que no hemos descubierto el PID (por ejemplo conexiones que no se originan en la máquina, o paquetes en mal estado).</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="581"/> + <source>Intercept Unknown Connections</source> + <translation type="obsolete">Interceptar conexiones desconocidas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="921"/> + <source>HostName</source> + <translation>Hostnaam</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1090"/> + <source>unix:///tmp/osui.sock</source> + <translation>unix:///tmp/osui.sock</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="975"/> + <source>until restart</source> + <translation>Tot herstart</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="980"/> + <source>always</source> + <translation>Altijd</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1102"/> + <source>/var/log/opensnitchd.log</source> + <translation>/var/log/opensnitchd.log</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1107"/> + <source>/dev/stdout</source> + <translation>/dev/stdout</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="879"/> + <source>Apply configuration to all nodes</source> + <translation>Instellingen toepassen op alle knooppunten</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1146"/> + <source>Database</source> + <translation>Databank</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1181"/> + <source>In memory</source> + <translation>In geheugen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1186"/> + <source>File</source> + <translation>Bestand</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1482"/> + <source>Close</source> + <translation>Sluiten</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1493"/> + <source>Apply</source> + <translation>Toepassen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1504"/> + <source>Save</source> + <translation>Opslaan</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="244"/> + <source>until reboot</source> + <translation>Tot herstart</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1200"/> + <source>Database type</source> + <translation>Soort databank</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1207"/> + <source>Select</source> + <translation>Kiezen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="83"/> + <source>Pop-ups default options</source> + <translation type="obsolete">Opciones por defecto de las ventanas emergentes</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="367"/> + <source>Pop-ups default position on screen</source> + <translation type="obsolete">Posición en pantalla</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="102"/> + <source><html><head/><body><p>The advanced view allows you to apply more filters on a connection</p><p>when a pop-up appears.</p></body></html></source> + <translation type="obsolete">La vista avanzada permite filtrar conexiones por más parámetros</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="359"/> + <source>Show advanced view by default</source> + <translation>Altijd uitgebreide meldingen tonen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="653"/> + <source>Action</source> + <translation>Actie</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="375"/> + <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source> + <translation><html><head/><body><p>Kruis aan om uitgebreide meldingen te tonen.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="343"/> + <source>Duration</source> + <translation>Duur</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="263"/> + <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source> + <translation><html><head/><body><p>Als een eenvoudige melding wordt getoond, dan kunt u verbindingen of programma's filteren op basis van een bepaalde eigenschap (uitvoerbaar bestand, poort, ip-adres, etc.).</p><p>Met deze opties heeft u keuze uit meerdere mogelijkheden om verbindingen te filteren.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="266"/> + <source>Filter connections also by:</source> + <translation>Verbindingen tevens filteren op:</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="362"/> + <source>If checked, this field will be checked when a pop-up is displayed</source> + <translation type="obsolete">Si lo seleccionas, este campo se usará para filtrar las conexiones</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="81"/> + <source>User ID</source> + <translation>Gebruikers-id</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="97"/> + <source>Destination port</source> + <translation>Bestemmingspoort</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="113"/> + <source>Destination IP</source> + <translation>Bestemmings-ip</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="463"/> + <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source> + <translation><html><head/><body><p>Deze time-out telt af tot het moment waarop u een melding te zien krijgt.</p><p>Als er geen keuze wordt gemaakt, dan worden de standaardopties gebruikt.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="356"/> + <source>The advanced view allows you to easily select multiple fields to filter connections</source> + <translation>In de uitgebreide weergave kunt u eenvoudig meerdere keuzes maken om verbindingen te filteren</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="110"/> + <source>If checked, this field will be selected when a pop-up is displayed</source> + <translation>Kruis aan om dit veld te kiezen als er een melding wordt getoond</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="159"/> + <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source> + <translation><html><head/><body><p>De standaard meldingsactie.</p><p>Als er een nieuwe uitgaande verbinding wordt opgezet, dan wordt standaard deze actie uitgevoerd als de time-out optreedt.</p><p><br/></p><p>Als een melding vraagt om een verbinding toe te staan of te weigeren:</p><p>1. nieuwe uitgaande verbindingen worden geweigerd;</p><p>2. bekende verbindingen worden toegestaan of geweigerd op basis van zelf-opgegeven regels.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="905"/> + <source>Default action when the GUI is disconnected</source> + <translation>Standaardactie bij geen grafisch programma</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1001"/> + <source>Debug invalid connections</source> + <translation>Fouten van slechte verbindingen opsporen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="39"/> + <source>Pop-ups</source> + <translation>Meldingen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="64"/> + <source>Default options</source> + <translation>Standaardopties</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="330"/> + <source>Default position on screen</source> + <translation>Standaard meldingspositie</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="769"/> + <source>any temporary rules</source> + <translation>Iedere tijdelijke regel</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="487"/> + <source><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Cuando esta opción está seleccionada, las reglas de la duración elegida no se añadirán a la lista de reglas temporales en la GUI.</p><p><br/></p><p>Las reglas temporales seguirán siendo válidas, y puedes usarlas cuando se pregunte para permitir o denegar una nueva conexión.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="490"/> + <source>Don't save rules of duration</source> + <translation type="obsolete">No guardar reglas de duración</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="463"/> + <source>Show events columns</source> + <translation type="obsolete">Mostrar columnas de la pestaña Eventos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="589"/> + <source>Time</source> + <translation>Tijdstip</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="669"/> + <source>Destination</source> + <translation>Bestemming</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="637"/> + <source>Protocol</source> + <translation>Protocol</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="685"/> + <source>Process</source> + <translation>Proces</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="605"/> + <source>Rule</source> + <translation>Regel</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="621"/> + <source>Node</source> + <translation>Knooppunt</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="723"/> + <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using wireguard.</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Si se selecciona opensnitch te preguntará para permitir o denegar conexiones que no tienen un PID asociado. Esto puede pasar por diferentes motivos, principalmente debido a conexiones inválidas.</p><p>La ventana emergente sólo contendrá información de la conexión.</p><p>Hay algunas situaciones en las que estas conexiones son válidas, por ejemplo al establecer un túnel VPN con wireguard.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="577"/> + <source>Events tab columns</source> + <translation>Gebeurteniskolommen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="308"/> + <source>by PID</source> + <translation>PID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="473"/> + <source>Disable pop-ups, only display a notification</source> + <translation>Interactieve meldingen uitschakelen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="496"/> + <source>Desktop notifications</source> + <translation>Meldingen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="514"/> + <source>Use system notifications</source> + <translation>Systeemmeldingen gebruiken</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="530"/> + <source>Use Qt notifications</source> + <translation>Qt-meldingen gebruiken</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="559"/> + <source>Test</source> + <translation>Uitproberen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="998"/> + <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source> + <translation><html><head/><body><p>Kruis aan om OpenSnitch te laten vragen of u verbindingen zonder PID wilt toestaan of weigeren. Dit kan bijvoorbeeld handig zijn bij verbindingen met een slechte status.</p><p>Het pop-upvenster bevat alleen informatie over de networkverbinding.</p><p>In sommige gevallen zijn deze verbindingen echter niet slecht, bijvoorbeeld bij het opzetten van een vpn met behulp van WireGuard.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1294"/> + <source>minutes</source> + <translation>minuten</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1326"/> + <source>Minutes between events purges</source> + <translation>Aantal minuten tussen gebeurtenisverwijderingen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1352"/> + <source>days</source> + <translation>dagen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1365"/> + <source>Maximum days of events to keep</source> + <translation>Aantal te behouden gebeurtenisdagen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="147"/> + <source>reject</source> + <translation>Afwijzen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="716"/> + <source>System</source> + <translation>Systeem</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="695"/> + <source>Command line</source> + <translation>Opdrachtregel</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="708"/> + <source>Theme</source> + <translation>Thema</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="219"/> + <source>30s</source> + <translation>30 sec.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="224"/> + <source>5m</source> + <translation>5 min.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="229"/> + <source>15m</source> + <translation>15 min.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="234"/> + <source>30m</source> + <translation>30 min.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="239"/> + <source>1h</source> + <translation>1 uur</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="748"/> + <source>Rules</source> + <translation>Regels</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="756"/> + <source>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI. + +Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</source> + <translation>Kruis aan om de regels van de gekozen duur niet toe te voegen aan de lijst met tijdelijke regels. + +De tijdelijke regels blijven echter geldig en kunnen worden gebruikt indien om een actie gevraagd wordt.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="761"/> + <source>Don't save/Delete rules of duration</source> + <translation>Regelduur niet onthouden/verwijderen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="779"/> + <source>30s or less</source> + <translation>30 sec. of korter</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="784"/> + <source>5m or less</source> + <translation>5 min. of korter</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="789"/> + <source>15m or less</source> + <translation>15 min. of korter</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="794"/> + <source>30m or less</source> + <translation>30 min. of korter</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="799"/> + <source>1h or less</source> + <translation>1 uur of korter</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="724"/> + <source>Language</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>ProcessDetailsDialog</name> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="14"/> + <source>Process details</source> + <translation>Procesinformatie</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="61"/> + <source>loading...</source> + <translation>Bezig met laden…</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="81"/> + <source>CWD: loading...</source> + <translation>CWD: bezig met laden…</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="93"/> + <source>mem stats: loading...</source> + <translation>Geheugenstatistieken: bezig met laden…</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="121"/> + <source>Status</source> + <translation>Status</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="135"/> + <source>Open files</source> + <translation>Geopende bestanden</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="149"/> + <source>I/O Statistics</source> + <translation>I/O-statistieken</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="163"/> + <source>Memory mapped files</source> + <translation>Bestanden in geheugen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="177"/> + <source>Stack</source> + <translation>Stapel</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="191"/> + <source>Environment variables</source> + <translation>Omgevingsvariabelen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="210"/> + <source>Application pids</source> + <translation>Programma-pid's</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="240"/> + <source>Start or stop monitoring this process</source> + <translation>Procesmonitoring starten/stoppen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="256"/> + <source>Close</source> + <translation>Sluiten</translation> + </message> +</context> +<context> + <name>RulesDialog</name> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="20"/> + <source>Rule</source> + <translation>Regel</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> + <source>Node</source> + <translation>Knooppunt</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> + <source>Apply rule to all nodes</source> + <translation>Regel toepassen op alle knooppunten</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="379"/> + <source>From this command line</source> + <translation>Van deze opdrachtregel</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="472"/> + <source>From this executable</source> + <translation>Van dit uitvoerbare bestand</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="56"/> + <source>Action</source> + <translation>Actie</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="138"/> + <source>/path/to/executable, .*/bin/executable[0-9\.]+$, ...</source> + <translation type="obsolete">/ruta/al/ejecutable, .*/bin/executable[0-9\.]+$, ...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="610"/> + <source>To this IP / Network</source> + <translation>Naar dit ip-adres/netwerk</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="97"/> + <source>once</source> + <translation>Eenmalig</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="230"/> + <source>until restart</source> + <translation type="obsolete">hasta reiniciar (el servicio)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="132"/> + <source>always</source> + <translation>Altijd</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="902"/> + <source>To this port</source> + <translation>Naar deze poort</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="372"/> + <source>From this user ID</source> + <translation>Van deze gebruikers-id</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/> + <source>Commas or spaces are not allowed to specify multiple domains. + +Use regular expressions instead: +.*(opensnitch|duckduckgo).com +.*\.google.com + +or a single domain: +www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... +gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source> + <translation>Komma's of spaties om meerdere domeinnamen op te geven zijn niet toegestaan. + +Gebruik in plaats daarvan reguliere uitdrukking: +.*(opensnitch|duckduckgo).com +.*\.google.com + +of een losse domeinnaam: +www.gnu.org - komt alleen overeen met www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... +gnu.org - komt alleen overeen met gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="603"/> + <source>www.domain.org, .*\.domain.org</source> + <translation>www.domein.org, .*\.domein.org</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="526"/> + <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source> + <translation><html><head/><body><p>Alleen tcp, udp en udplite toegestaan</p><p>U kunt reguliere uitdrukkingen gebruiken, zoals ^(TCP|UDP)$</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="532"/> + <source>TCP</source> + <translation>Tcp</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="760"/> + <source>You can specify a single IP: +- 192.168.1.1 + +or a regular expression: +- 192\.168\.1\.[0-9]+ + +multiple IPs: +- ^(192\.168\.1\.1|172\.16\.0\.1)$ + +You can also specify a subnet: +- 192.168.1.0/24 + +Note: Commas or spaces are not allowed to separate IPs or networks.</source> + <translation>U kunt een los ip-adres opgeven: +- 192.168.1.1 + +of een reguliere uitdrukking: +- 192\.168\.1\.[0-9]+ + +meerdere ip-adressen: +- ^(192\.168\.1\.1|172\.16\.0\.1)$ + +Een subnet is ook mogelijk: +- 192.168.1.0/24 + +Let op: komma's en spaties om adressen en netwerken te scheiden zijn niet toegestaan.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="89"/> + <source>Duration</source> + <translation>Duur</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="633"/> + <source>Protocol</source> + <translation>Protocol</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="750"/> + <source>To this host</source> + <translation>Naar deze host</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="151"/> + <source>Deny</source> + <translation>Weigeren</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="191"/> + <source>Allow</source> + <translation>Toestaan</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="245"/> + <source>Name</source> + <translation type="unfinished">Nombre</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="207"/> + <source>Enable</source> + <translation>Inschakelen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="238"/> + <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them. + +000-allow-localhost +001-deny-broadcast +...</source> + <translation>De regels worden op alfabetische volgorde uitgevoerd, dus geef ze een naam die prioriteit aanduidt. + +000-allow-localhost +001-deny-broadcast +…</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="773"/> + <source>leave blank to autocreate</source> + <translation type="obsolete">dejar en blanco para autoasignar nombre</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="214"/> + <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one. + +You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example: + +[x] Priority - 000-priority-rule +[ ] Priority - 001-less-priority-rule</source> + <translation>Kruis aan om deze regel boven de rest te laten gaan. Na deze regels worden geen nieuwe meer aangekruist. + +Let op: regels worden in alfabetische volgorde uitgevoerd. Voorbeeld: + +[x] Prioriteit - 000-priority-rule +[ ] Prioriteit - 001-less-priority-rule</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="222"/> + <source>Priority rule</source> + <translation>Prioriteitsregel</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1092"/> + <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source> + <translation><html><head/><body><p>Standaard zijn regels hoofdlettergevoelig. Als een proces dus verbinding wil maken met gOOgle.CoM en u een regel hebt opgegeven om .*google.com te weigeren, dan wordt de verbinding geblokkeerd.<br/></p><p>Als u deze optie aankruist, dan dient u de exacte tekenreeks (domeinnaam, uitvoerbaar bestand, opdrachtregel) die u wilt filteren op te geven.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1095"/> + <source>Case-sensitive</source> + <translation>Hoofdlettergevoelig</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="686"/> + <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>U kunt met behulp van reguliere uitdrukkingen meerdere poorten opgeven:</p><p><br/></p><p>- 53, 80 of 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 of 5551, 5552, 5553, etc.:</p><p>^(53|443|555[0-9])$</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="127"/> + <source>until reboot</source> + <translation>Tot herstart</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="980"/> + <source>To this list of domains</source> + <translation>Naar deze lijst met domeinnamen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="539"/> + <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Selecciona un directorio con listas de dominios para permitir o denegar.</p><p>Mete dentro de este directorio ficheros con cualquier extensión que contengan listas de dominios.</p><p><br/>El formato de cada dominio de la lista tiene que estar en formato hosts, así:</p><p>127.0.0.1 www.domain.com</p><p>o </p><p>0.0.0.0 www.domain.com</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="346"/> + <source>Applications</source> + <translation>Programma's</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="216"/> + <source><html><head/><body><p>This field will only match the executable path. It is not modifiable by the user.<br/></p><p>You can use regular expressions to deny executions from /tmp for example:<br/></p><p>^/tmp/.*$</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Este campo sólo comprueba la ruta del ejecutable (la cual no es modificable por el usuario).<br/></p><p>Puedes usar expresiones regulares para denegar cualquier ejecución desde /tmp, por ejemplo; ^/tmp/.*$</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="389"/> + <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source> + <translation><html><head/><body><p>Dit veld bevat de door de gebruiker uitgevoerde opdrachtregel.<br/></p><p>Als de gebruiker een opdracht invoert, dan wordt alleen de opdracht getoond:</p><p>telnet 1.2.3.4<br/></p><p>Als de gebruiker een directe of relatieve opdrachtlocatie opgeeft, dan wordt het volgende getoond:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="399"/> + <source>From this PID</source> + <translation>Van deze PID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="491"/> + <source>Network</source> + <translation>Netwerk</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="932"/> + <source>List of domains/IPs</source> + <translation>Lijst met domeinen/ip's</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="938"/> + <source>To this list of network ranges</source> + <translation>Naar deze lijst met netwerkreeksen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="945"/> + <source>To this list of IPs</source> + <translation>Naar deze lijst met ip-adressen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="971"/> + <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation><html><head/><body><p>Kies een map met bestanden die een lijst met toe te stane of te weigeren ip-adressen bevat:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>Eén ip-adres per regel. Blanco regels of regels beginnend met # worden genegeerd.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1006"/> + <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation><html><head/><body><p>Kies een map met bestanden die een lijst met toe te stane of te weigeren netwerkreeksen bevat:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p></p>.</p><p>etc.<br/></p><p>Eén reeks per regel. Blanco regels of regels beginnend met # worden genegeerd.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1034"/> + <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source> + <translation><html><head/><body><p>Kies een map met lijsten met toe te stane of te weigeren domeinnamen.</p><p>Voorzie de map van bestanden met welke extensie dan ook.</p><p><br/>De opmaak van elke regel is als volgt (hostsopmaak):</p><p>127.0.0.1 www.domein.nl</p><p>of </p><p>0.0.0.0 www.domein.nl</p><p>Blanco regels of regels beginnend met # worden genegeerd.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1049"/> + <source>To this list of domains +(regular expressions)</source> + <translation>Naar deze lijst met domeinnamen +(reguliere uitdrukkingen)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1076"/> + <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation><html><head/><body><p>Kies een map met reguliere-uitdrukkingsbestanden met toe te stane of te weigeren domeinnamen.</p><p>.*\.voorbeeld\.nl.</p><p>U kunt ook een volledige domeinnaam gebruiken: &quot;voorbeeld.nl&quot; , die vervolgens overeenkomt met iets.voorbeeld.nl, iets.voorbeeld.nl.localdomain, etc.</p><p>Eén domeinnaam per regel. Blanco regels of regels beginnend met # worden genegeerd.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="168"/> + <source>Reject</source> + <translation>Afwijzen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1151"/> + <source>Description...</source> + <translation>Beschrijving…</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="355"/> + <source><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></source> + <translation><html><head/><body><p>De waarde van dit veld is altijd de directe locatie naar het uitvoerbare bestand: /locatie/van/bestand<br/></p><p>Voorbeelden:</p><p>- Eenvoudig: /locatie/van/bestand</p><p>- Meerdere locaties: ^/usr/lib(64|)/firefox/firefox$</p><p>- Meerdere bestanden: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Weiger-/Toestaanacties met behulp van /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>Bekijk meer voorbeelden op onze <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wikipagina</a> of vraag hulp ons <a href="https://github.com/evilsocket/opensnitch/discussions">forum</a>.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="365"/> + <source>Is regular expression</source> + <translation>Is een reguliere uitdrukking</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> + <source>is regular expression</source> + <translation>Is een reguliere uitdrukking</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="863"/> + <source>Network interface</source> + <translation>Netwerkinterface</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1086"/> + <source>More</source> + <translation>Overig</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1102"/> + <source>Don't log connections that match this rule</source> + <translation>Leg met deze regel overeenkomende verbindingen niet vast in het logboek</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1105"/> + <source>Don't log connections</source> + <translation>Verbindingen niet vastleggen in logboek</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="148"/> + <source>Deny will just discard the connection</source> + <translation>Weigeren negeert de verbinding</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="165"/> + <source>Reject will drop the connection, and kill the socket that initiated it</source> + <translation>Afwijzen wijst de verbinding af en sluit de socket in kwestie af</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="185"/> + <source>Allow will allow the connection</source> + <translation>Toestaan staat de verbinding toe</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="83"/> + <source>Name (leave blank to autocreate)</source> + <translation type="obsolete">Naam (laat leeg om automatisch aan te maken)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="566"/> + <source>ICMP</source> + <translation>Icmp</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="571"/> + <source>ICMP6</source> + <translation>Icmp6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="576"/> + <source>SCTP</source> + <translation>Sctp</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="581"/> + <source>SCTP6</source> + <translation>Sctp6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="937"/> + <source>Color</source> + <translation type="obsolete">Kleur</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="743"/> + <source>From this IP / Network</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="872"/> + <source>From this port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="918"/> + <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>StatsDialog</name> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="34"/> + <source>OpenSnitch Network Statistics</source> + <translation>OpenSnitch-netwerkstatistieken</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="287"/> + <source>Save to CSV</source> + <translation type="obsolete">Exportar a CSV.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="297"/> + <source>Ctrl+S</source> + <translation type="obsolete">Ctrl+S</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="333"/> + <source>Create a new rule</source> + <translation>Nieuwe regel opstellen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="376"/> + <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source> + <translation><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">Hostnaam - 192.168.1.1</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="413"/> + <source>Status</source> + <translation>Status</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1793"/> + <source>-</source> + <translation>-</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="451"/> + <source>Start or Stop interception</source> + <translation>Onderscheppen aan/uit</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="496"/> + <source>Events</source> + <translation>Gebeurtenissen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="94"/> + <source>Filter</source> + <translation>Filter</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="107"/> + <source>Allow</source> + <translation>Toestaan</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="116"/> + <source>Deny</source> + <translation>Weigeren</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="143"/> + <source>Ex.: firefox</source> + <translation>Bijv. firefox</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="205"/> + <source>50</source> + <translation>50</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="210"/> + <source>100</source> + <translation>100</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="215"/> + <source>200</source> + <translation>200</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="220"/> + <source>300</source> + <translation>300</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="826"/> + <source>Nodes</source> + <translation>Knooppunten</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="554"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Addr column to view details of a node)</span></p></body></html></source> + <translation type="obsolete">(doble click en la columna Dirección para ver los detalles)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1700"/> + <source>Rules</source> + <translation>Regels</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="995"/> + <source>enable</source> + <translation>Inschakelen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="684"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Name column to view details of a rule)</span></p></body></html></source> + <translation type="obsolete">(doble click en la columna Nombre para ver los detalles)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="692"/> + <source>search rule name</source> + <translation type="obsolete">buscar regla</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="782"/> + <source>Application rules</source> + <translation>Programmaregels</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="936"/> + <source>Permanent</source> + <translation>Permanent</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="945"/> + <source>Temporary</source> + <translation>Tijdelijk</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1063"/> + <source>Hosts</source> + <translation>Hosts</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1364"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click to view details of an item)</span></p></body></html></source> + <translation type="obsolete">(doble click en un dominio para ver detalles)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1153"/> + <source>Applications</source> + <translation>Programma's</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1266"/> + <source>Addresses</source> + <translation>Adressen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1356"/> + <source>Ports</source> + <translation>Poorten</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1440"/> + <source>Users</source> + <translation>Gebruikers</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1544"/> + <source>Connections</source> + <translation>Verbindingen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1596"/> + <source>Dropped</source> + <translation>Afgewezen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1648"/> + <source>Uptime</source> + <translation>Uptime</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1767"/> + <source>Version</source> + <translation>Versie</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="233"/> + <source>Delete all intercepted events</source> + <translation>Alle onderschepte gebeurtenissen wissen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1022"/> + <source>Edit rule</source> + <translation>Regel bewerken</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1039"/> + <source>Delete rule</source> + <translation>Regel verwijderen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="926"/> + <source>Delete all intercepted hosts</source> + <translation type="obsolete">Borrar todos los hosts</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1051"/> + <source>Delete all intercepted applications</source> + <translation type="obsolete">Borrar todos las aplicaciones</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1159"/> + <source>Delete all intercepted addresses</source> + <translation type="obsolete">Borrar todas las direcciones</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1261"/> + <source>Delete all intercepted ports</source> + <translation type="obsolete">Borrar todos los puertos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1371"/> + <source>Delete all intercepted users</source> + <translation type="obsolete">Borrar todos los usuarios</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="699"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click on a row to view details of a rule)</span></p></body></html></source> + <translation type="obsolete">(Doble click en una fila para editar una regla)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="912"/> + <source>Delete connections that matched this rule</source> + <translation type="obsolete">Borrar conexiones que coinciden con esta regla</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="927"/> + <source>All applications</source> + <translation>Alle programma's</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="125"/> + <source>Reject</source> + <translation>Afwijzen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="180"/> + <source>0</source> + <translation>0</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="777"/> + <source>2</source> + <translation>2</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="954"/> + <source>System rules</source> + <translation>Systeemregels</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="637"/> + <source>Delete this node</source> + <translation>Knooppunt verwijderen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="653"/> + <source>Show the preferences of this node</source> + <translation>Knooppuntvoorkeuren openen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="669"/> + <source>Start or stop interception of this node</source> + <translation>Onderscheppen van knooppunt aan/uit</translation> + </message> +</context> +<context> + <name>contextual_menu</name> + <message> + <location filename="../../../opensnitch/service.py" line="47"/> + <source>Statistics</source> + <translation>Statistieken</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="50"/> + <source>Help</source> + <translation>Hulp</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="51"/> + <source>Close</source> + <translation>Sluiten</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="48"/> + <source>Enable</source> + <translation>Inschakelen</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="49"/> + <source>Disable</source> + <translation>Uitschakelen</translation> + </message> +</context> +<context> + <name>firewall</name> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="91"/> + <source>Configuration applied.</source> + <translation>De instellingen zijn toegepast.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="404"/> + <source>Error: {0}</source> + <translation>Foutmelding: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="193"/> + <source>Applying changes...</source> + <translation>Bezig met toepassen van wijzigingen…</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="230"/> + <source>Error getting INPUT chain policy</source> + <translation>Foutmelding tijdens opvragen van INVOERbeleid</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="237"/> + <source>Error getting OUTPUT chain policy</source> + <translation>Foutmelding tijdens opvragen van UITVOERbeleid</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="290"/> + <source>In order to configure firewall rules from the GUI, we need to use 'nftables' instead of 'iptables'</source> + <translation>De firewallregels kunnen in het grafische programma alléén worden ingesteld met ‘nftables’ en niet met ‘iptables’</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="304"/> + <source>Enabling firewall...</source> + <translation>Bezig met inschakelen van firewall…</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="306"/> + <source>Disabling firewall...</source> + <translation>Bezig met uitschakelen van firewall…</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="71"/> + <source>Dest Port</source> + <translation>Bestemmingspoort</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="72"/> + <source>Source Port</source> + <translation>Bronpoort</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="73"/> + <source>Dest IP</source> + <translation>Bestemmings-ip</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="74"/> + <source>Source IP</source> + <translation>Bron-ip</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="75"/> + <source>Input interface</source> + <translation>Invoerinterface</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="76"/> + <source>Output interface</source> + <translation>Uitvoerinterface</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="77"/> + <source>Set conntrack mark</source> + <translation>Conntrackmarkering instellen</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="78"/> + <source>Match conntrack mark</source> + <translation>Conntrackmarkering overeen laten komen</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="79"/> + <source>Match conntrack state(s)</source> + <translation>Conntrackstatus(sen) overeen laten komen</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="80"/> + <source>Set mark on packet</source> + <translation>Pakket markeren</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="81"/> + <source>Match packet information</source> + <translation>Pakketinformatie markeren</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="87"/> + <source>Bandwidth quotas</source> + <translation>Bandbreedtequota</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="89"/> + <source>Rate limit connections</source> + <translation>Verbindingen met beperkt gebruik</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="373"/> + <source>Your protobuf version is incompatible, you need to install protobuf 3.8.0 or superior +(pip3 install --ignore-installed protobuf==3.8.0)</source> + <translation>Uw protobuf-version is incompatibel. Installeer protobuf 3.8.0 of nieuwer +(pip3 install --ignore-installed protobuf==3.8.0)</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="397"/> + <source>Rule deleted</source> + <translation>De regel is verwijderd</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="401"/> + <source>Rule added</source> + <translation>De regel is toegevoegd</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="420"/> + <source>You can use ',' or '-' to specify multiple ports/IPs or ranges/values:<br><br>ports: 22 or 22,443 or 50000-60000<br>IPs: 192.168.1.1 or 192.168.1.30-192.168.1.130<br>Values: echo-reply,echo-request<br>Values: new,established,related</source> + <translation>U kunt gebruikmaken van ‘,’ of ‘-’ om meerdere poorten/ip-adressen of reeksen/waarden op te geven:<br><br>Poorten: 22 of 22,443 of 50000-60000<br>Ip-adressen: 192.168.1.1 of 192.168.1.30-192.168.1.130<br>Waarden: echo-reply,echo-request<br>Waarden: new,established,related</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="440"/> + <source>Deleting rule, wait</source> + <translation>Bezig met verwijderen van regel…</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="443"/> + <source>Error updating rule</source> + <translation>De regel kan niet worden bijgewerkt</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="483"/> + <source>Adding rule, wait</source> + <translation>Bezig met toevoegen van regel…</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="492"/> + <source><select a statement></source> + <translation><Kies een uitdrukking></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="787"/> + <source>Equal</source> + <translation>Gelijk</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="788"/> + <source>Not equal</source> + <translation>Niet gelijk</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="789"/> + <source>Greater or equal than</source> + <translation>Groter dan of gelijk aan</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="790"/> + <source>Greater than</source> + <translation>Groter dan</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="791"/> + <source>Less or equal than</source> + <translation>Kleiner dan of gelijk aan</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="792"/> + <source>Less than</source> + <translation>Kleiner dan</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1350"/> + <source>Firewall rule</source> + <translation>Firewallregel</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="885"/> + <source>Simple</source> + <translation>Eenvoudig</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="890"/> + <source>Advanced</source> + <translation>Uitgebreid</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1046"/> + <source>This rule is not supported yet.</source> + <translation>Deze regel wordt nog niet ondersteund.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1111"/> + <source>Exclude service</source> + <translation>Dienst uitsluiten</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1123"/> + <source>Allow inbound connections to the selected port.</source> + <translation>Sta inkomende verbindingen toe op de gekozen poort.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1125"/> + <source>Allow outbound connections to the selected port.</source> + <translation>Sta uitgaande verbindingen toe op de gekozen poort.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1201"/> + <source>select a statement.</source> + <translation>Kies een uitdrukking.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1217"/> + <source>value cannot be 0 or empty.</source> + <translation>De waarde mag niet 0 of blanco zijn.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1229"/> + <source>the value format is 1024/kbytes (or bytes, mbytes, gbytes)</source> + <translation>De waarde-opmaak is 1024/kbytes (of bytes, mbytes, gbytes)</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1240"/> + <source>the value format is 1024/kbytes/second (or bytes, mbytes, gbytes)</source> + <translation>De waarde-opmaak is 1024/kbytes/second (of bytes, mbytes, gbytes)</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1243"/> + <source>rate-limit not valid, use: bytes, kbytes, mbytes or gbytes.</source> + <translation>De beperking is ongeldig - gebruik bytes, mbytes of gbytes.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1245"/> + <source>time-limit not valid, use: second, minute, hour or day</source> + <translation>De beperking is ongeldig - gebruik second, minute, hour of day</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1293"/> + <source>port not valid.</source> + <translation>De poort is ongeldig.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="108"/> + <source> +Supported formats: + + - Simple: 23 + - Ranges: 80-1024 + - Multiple ports: 80,443,8080 +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="134"/> + <source> +Supported formats: + + - Simple: 1.2.3.4 + - IP ranges: 1.2.3.100-1.2.3.200 + - Network ranges: 1.2.3.4/24 +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="147"/> + <source>Match input interface. Regular expressions not allowed.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="154"/> + <source>Match output interface. Regular expressions not allowed.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="161"/> + <source>Set a conntrack mark on the connection, in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="171"/> + <source>Match a conntrack mark of the connection, in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="178"/> + <source>Match conntrack states. + +Supported formats: + - Simple: new + - Multiple states separated by commas: related,new +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="193"/> + <source> +Match packet's metainformation. + +Value must be in decimal format, except for the "l4proto" option. +For l4proto it can be a lower case string, for example: + tcp + udp + icmp, + etc + +If the value is decimal for protocol or lproto, it'll use it as the code of +that protocol. +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="213"/> + <source>Set a mark on the packet matching the specified conditions. The value is in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="221"/> + <source> +Match ICMP codes. + +Supported formats: + - Simple: echo-request + - Multiple separated by commas: echo-request,echo-reply +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="234"/> + <source> +Match ICMPv6 codes. + +Supported formats: + - Simple: echo-request + - Multiple separated by commas: echo-request,echo-reply +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="247"/> + <source>Print a message when this rule matches a packet.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="254"/> + <source> +Apply quotas on connections. + +For example when: + - "quota over 10/mbytes" -> apply the Action defined (DROP) + - "quota until 10/mbytes" -> apply the Action defined (ACCEPT) + +The value must be in the format: VALUE/UNITS, for example: + - 10mbytes, 1/gbytes, etc +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="286"/> + <source> +Apply limits on connections. + +For example when: + - "limit over 10/mbytes/minute" -> apply the Action defined (DROP, ACCEPT, etc) + (When there're more than 10MB per minute, apply an Action) + + - "limit until 10/mbytes/hour" -> apply the Action defined (ACCEPT) + +The value must be in the format: VALUE/UNITS/TIME, for example: + - 10/mbytes/minute, 1/gbytes/hour, etc +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="607"/> + <source>num</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="621"/> + <source>to</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>menu_close</name> + <message> + <location filename="../../../opensnitch/service.py" line="131"/> + <source>Close</source> + <translation type="obsolete">Cerrar</translation> + </message> +</context> +<context> + <name>menu_help</name> + <message> + <location filename="../../../opensnitch/service.py" line="126"/> + <source>Help</source> + <translation type="obsolete">Ayuda</translation> + </message> +</context> +<context> + <name>menu_statistics</name> + <message> + <location filename="../../../opensnitch/service.py" line="120"/> + <source>Statistics</source> + <translation type="obsolete">Eventos</translation> + </message> +</context> +<context> + <name>messages</name> + <message> + <location filename="../../../opensnitch/service.py" line="281"/> + <source>Info</source> + <translation>Informatie</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="285"/> + <source>Error</source> + <translation>Foutmelding</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="289"/> + <source>Warning</source> + <translation>Waarschuwing</translation> + </message> +</context> +<context> + <name>notifications</name> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="654"/> + <source>System notifications are not available, you need to install python3-notify2.</source> + <translation>Systeemmeldingen zijn niet beschikbaar - installeer python3-notify2.</translation> + </message> +</context> +<context> + <name>popups</name> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="115"/> + <source>Allow</source> + <translation>Toestaan</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="114"/> + <source>Deny</source> + <translation>Weigeren</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/> + <source>forever</source> + <translation>Oneindig</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="331"/> + <source>Outgoing connection</source> + <translation>Uitgaande verbinding</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="336"/> + <source>Process launched from:</source> + <translation>Proces gestart via:</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="377"/> + <source>from this command line</source> + <translation>deze opdrachtregel</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="373"/> + <source>from this executable</source> + <translation>dit uitvoerbare bestand</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="208"/> + <source>Unknown process</source> + <translation type="obsolete">Proceso no encontrado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="50"/> + <source>until reboot</source> + <translation>Tot herstart</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="379"/> + <source>to port {0}</source> + <translation>naar poort {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="222"/> + <source><b>%s</b> is connecting to <b>%s</b> on %s port %d</source> + <translation type="obsolete"><b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="228"/> + <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source> + <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="442"/> + <source>to {0}</source> + <translation>naar {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="382"/> + <source>from user {0}</source> + <translation>naar gebruiker {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="399"/> + <source>to {0}.*</source> + <translation>naar {0}.*</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="452"/> + <source>to *.{0}</source> + <translation>naar *.{0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="378"/> + <source>to *{0}</source> + <translation type="obsolete">a *{0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="486"/> + <source><b>Remote</b> process %s running on <b>%s</b></source> + <translation><b>Extern</b> proces %s actief op <b>%s</b></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="490"/> + <source>is connecting to <b>%s</b> on %s port %d</source> + <translation>maakt verbinding met <b>%s</b> op %s poort %d</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="502"/> + <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source> + <translation>tracht <b>%s</b> te herleiden via %s, %s poort %d</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="386"/> + <source>from this PID</source> + <translation>van deze PID</translation> + </message> + <message> + <location filename="../../../opensnitch/notifications.py" line="122"/> + <source>New outgoing connection</source> + <translation>Nieuwe uitgaande verbinding</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="116"/> + <source>Reject</source> + <translation>Afwijzen</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="497"/> + <source>is connecting to <b>%s</b>, %s</source> + <translation>maakt verbinding met <b>%s</b>, %s</translation> + </message> + <message> + <location filename="../../../opensnitch/notifications.py" line="42"/> + <source>Open</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>popups2</name> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="254"/> + <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source> + <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> + </message> +</context> +<context> + <name>preferences</name> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="171"/> + <source>Exception saving config: %s</source> + <translation type="obsolete">Error al guarda la configuración: %s</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="177"/> + <source>Applying configuration on %s ...</source> + <translation type="obsolete">Aplicando configuración en %s ...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="292"/> + <source>Server address can not be empty</source> + <translation>Het serveradres mag niet blanco zijn</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="227"/> + <source>Error loading %s configuration</source> + <translation type="obsolete">Error al cargar la configuración %s</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="568"/> + <source>Configuration applied.</source> + <translation>De instellingen zijn toegepast.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="257"/> + <source>Error applying configuration: %s</source> + <translation type="obsolete">Error al aplicar la configuración: %s</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="386"/> + <source>Exception saving config: {0}</source> + <translation>Uitzondering tijdens opslaan van instellingen: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="500"/> + <source>Applying configuration on {0} ...</source> + <translation>Bezig met toepassen van instellingen op {0}…</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="321"/> + <source>Error loading {0} configuration</source> + <translation>Foutmelding tijdens laden van {0}-instellingen</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="570"/> + <source>Error applying configuration: {0}</source> + <translation>Foutmelding tijdens toepassen van instellingen: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> + <source>Warning</source> + <translation>Waarschuwing</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> + <source>You must select a file for the database<br>or choose "In memory" type.</source> + <translation>Kies een databankbestand<br>of ‘In geheugen’.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="401"/> + <source>DB type changed</source> + <translation>Het soort DB is gewijzigd</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="38"/> + <source>Restart the GUI in order effects to take effect</source> + <translation>Herstart het programma om de wijzigingen toe te passen.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="607"/> + <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source> + <translation>Houd de cursor boven teksten om hulpballonnen te tonen<br><br>Bekijk ook de wiki: <a href="{0}">{0}</a></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="466"/> + <source>System</source> + <translation>Systeem</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="187"/> + <source>Themes not available. Install qt-material: pip3 install qt-material</source> + <translation>Er zijn geen thema's beschikbaar. Installeer qt-material: pip3 install qt-material</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> + <source>UI theme changed</source> + <translation>Het thema is gewijzigd</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> + <source>Restart the GUI in order to apply the new theme</source> + <translation>Herstart het programma om het nieuwe thema toe te passen.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="508"/> + <source>Ok</source> + <translation>Oké</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="472"/> + <source>Restart the GUI in order changes to take effect</source> + <translation type="obsolete">Herstart het programma om de wijzigingen toe te passen</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="388"/> + <source>There're no nodes connected</source> + <translation>Er zijn geen knooppunten verbonden</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="520"/> + <source>Exception saving node config {0}: {1}</source> + <translation>Uitzondering tijdens opslaan van knooppuntinstellingen {0}: {1}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="164"/> + <source>System default</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="433"/> + <source>Language changed</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>proc_details</name> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="100"/> + <source><b>Error loading process information:</b> <br><br> + +</source> + <translation><b>Foutmelding tijdens laden van procesinformatie:</b> <br><br> + +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="119"/> + <source><b>Error stopping monitoring process:</b><br><br></source> + <translation><b>Foutmelding tijdens stoppen van monitorproces:</b> <br><br></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="159"/> + <source>loading...</source> + <translation>Bezig met laden…</translation> + </message> +</context> +<context> + <name>rules</name> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="228"/> + <source>There're no nodes connected.</source> + <translation>Er zijn geen knooppunten verbonden.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="271"/> + <source>Rule applied.</source> + <translation>De regel is toegepast.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="123"/> + <source>Error applying rule: %s</source> + <translation type="obsolete">Error al aplicar la regla: %s</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="641"/> + <source>protocol can not be empty, or uncheck it</source> + <translation>Het protocol mag niet blanco zijn</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="655"/> + <source>Protocol regexp error</source> + <translation>Reguliere-uitdrukkingsfout in protocol</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="659"/> + <source>process path can not be empty</source> + <translation>De proceslocatie mag niet blanco zijn</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="673"/> + <source>Process path regexp error</source> + <translation>Reguliere-uitdrukkingsfout in proceslocatie</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="677"/> + <source>command line can not be empty</source> + <translation>De opdrachtregel mag niet blanco zijn</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="691"/> + <source>Command line regexp error</source> + <translation>Reguliere-uitdrukkingsfout in opdrachtregel</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="731"/> + <source>Dest port can not be empty</source> + <translation>De bestemmingspoort mag niet blanco zijn</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="745"/> + <source>Dst port regexp error</source> + <translation>Reguliere-uitdrukkingsfout in bestemmingspoort</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="749"/> + <source>Dest host can not be empty</source> + <translation>De bestemmingspoort mag niet blanco zijn</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="763"/> + <source>Dst host regexp error</source> + <translation>Reguliere-uitdrukkingsfout in bestemmingspoort</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="805"/> + <source>Dest IP/Network can not be empty</source> + <translation>De bestemmings-ip/-netwerk mag niet blanco zijn</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="831"/> + <source>Dst IP regexp error</source> + <translation>Reguliere-uitdrukkingsfout in bestemmings-ip/-netwerk</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="843"/> + <source>User ID can not be empty</source> + <translation>De gebruikers-id mag niet blanco zijn</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="857"/> + <source>User ID regexp error</source> + <translation>Reguliere-uitdrukkingsfout in gebruikers-id</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="273"/> + <source>Error applying rule: {0}</source> + <translation>De regel kan niet worden toegepast: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="931"/> + <source>Lists field cannot be empty</source> + <translation>Het lijstveld mag niet blanco zijn</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="933"/> + <source>Lists field must be a directory</source> + <translation>Het lijstveld dient een map te zijn</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="976"/> + <source><b>Rule not supported</b></source> + <translation><b>Deze regel wordt niet ondersteund</b></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="539"/> + <source><b>Error loading rule</b></source> + <translation><b>De regel kan niet worden geladen</b></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="245"/> + <source>There's already a rule with this name.</source> + <translation>Er is al een regel met deze naam.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="861"/> + <source>PID field can not be empty</source> + <translation>Het PID-veld mag niet blanco zijn</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="875"/> + <source>PID field regexp error</source> + <translation>Reguliere-uitdrukkingsfout in PID-veld</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="963"/> + <source>Select at least one field.</source> + <translation>Kies minimaal één veld.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="695"/> + <source>Network interface can not be empty</source> + <translation>De netwerkinterface mag niet blanco zijn</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="709"/> + <source>Network interface regexp error</source> + <translation>Reguliere-uitdrukkingsfout in netwerkinterface</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="713"/> + <source>Source port can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="727"/> + <source>Source port regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="767"/> + <source>Source IP/Network can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="793"/> + <source>Source IP regexp error</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>stats</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="313"/> + <source>Not running</source> + <translation>Inactief</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="314"/> + <source>Disabled</source> + <translation>Uitgeschakeld</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="315"/> + <source>Running</source> + <translation>Actief</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="412"/> + <source>OpenSnitch Network Statistics</source> + <translation type="obsolete">Eventos de OpenSnitch</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="414"/> + <source>OpenSnitch Network Statistics for</source> + <translation type="obsolete">Eventos de OpenSnitch de</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1189"/> + <source> Your are about to delete this rule. </source> + <translation> U staat op het punt om deze regel te verwijderen. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> + <source> Are you sure?</source> + <translation> Weet u het zeker?</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="636"/> + <source>OpenSnitch Network Statistics {0}</source> + <translation>OpenSnitch-netwerkstatistieken {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="638"/> + <source>OpenSnitch Network Statistics for {0}</source> + <translation>OpenSnitch-netwerkstatistieken van {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> + <source>Name</source> + <translation type="obsolete">Nombre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Address</source> + <translation type="obsolete">Dirección</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="176"/> + <source>Status</source> + <translation type="obsolete">Estado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="177"/> + <source>Hostname</source> + <translation type="obsolete">Hostname</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="183"/> + <source>Version</source> + <translation type="obsolete">Versión</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="834"/> + <source>Rules</source> + <translation type="unfinished">Reglas</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Time</source> + <translation type="obsolete">Hora</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="875"/> + <source>Action</source> + <translation type="unfinished">Acción</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> + <source>Duration</source> + <translation type="obsolete">Duración</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> + <source>Node</source> + <translation type="obsolete">Nodo</translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="18"/> + <source>Hits</source> + <translation>Tikken</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>Protocol</source> + <translation type="obsolete">Protocolo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2581"/> + <source>Save as CSV</source> + <translation>Opslaan als csv-bestand</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Enabled</source> + <translation type="obsolete">Habilitado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="961"/> + <source>Delete</source> + <translation>Verwijderen</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="948"/> + <source>always</source> + <translation type="obsolete">siempre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="580"/> + <source><b>Error:</b><br><br>{0}</source> + <translation type="obsolete"><b>Error:</b><br><br>{0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="954"/> + <source>Disable</source> + <translation>Uitschakelen</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="956"/> + <source>Enable</source> + <translation>Inschakelen</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="959"/> + <source>Duplicate</source> + <translation>Klonen</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="960"/> + <source>Edit</source> + <translation>Bewerken</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1248"/> + <source>Rule not found by that name and node</source> + <translation>Er is geen regel met die naam en dat knooppunt</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1301"/> + <source><b>Error:</b><br><br></source> + <comment>{0}</comment> + <translation><b>Foutmelding:</b><br><br></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1308"/> + <source>Warning:</source> + <translation>Waarschuwing:</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="940"/> + <source>Allow</source> + <translation>Toestaan</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="941"/> + <source>Deny</source> + <translation>Weigeren</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="945"/> + <source>Always</source> + <translation>Altijd</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="946"/> + <source>Until reboot</source> + <translation>Tot herstart</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> + <source> You are about to delete this rule. </source> + <translation> U staat op het punt om deze regel te verwijderen. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> + <source>Process</source> + <translation type="obsolete">Aplicación</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> + <source>Destination</source> + <translation type="obsolete">Destino</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> + <source>Rule</source> + <translation type="obsolete">Regla</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> + <source>UserID</source> + <translation type="obsolete">UserID</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="174"/> + <source>LastConnection</source> + <translation type="obsolete">ÚltimaConexión</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> + <source>Name</source> + <comment>xxxxx</comment> + <translation type="obsolete">Nombre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> + <source>Name</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Nombre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Address</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Dirección</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> + <source>Status</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Estado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> + <source>Hostname</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Hostname</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> + <source>Version</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Versión</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> + <source>Rules</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Reglas</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Time</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Hora</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> + <source>Action</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Acción</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> + <source>Duration</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Duración</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> + <source>Node</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Nodo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Enabled</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Habilitado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> + <source>Hits</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Total</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>Protocol</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Protocolo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> + <source>Process</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Aplicación</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> + <source>Destination</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Destino</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> + <source>Rule</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Regla</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> + <source>UserID</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">UserID</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> + <source>LastConnection</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">ÚltimaConexión</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="287"/> + <source>Name</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Naam</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="288"/> + <source>Address</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Adres</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="289"/> + <source>Status</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Status</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="290"/> + <source>Hostname</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Hostnaam</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="423"/> + <source>Version</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Versie</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="420"/> + <source>Rules</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Regels</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Time</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Tijdstip</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> + <source>Action</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Actie</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> + <source>Duration</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Duur</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> + <source>Node</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Knooppunt</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Enabled</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Ingeschakeld</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="438"/> + <source>Hits</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Tikken</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> + <source>Protocol</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Protocol</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Process</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Proces</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>Destination</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Bestemming</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> + <source>Rule</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Regel</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> + <source>UserID</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Gebruikers-id</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> + <source>LastConnection</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>RecentsteVerbinding</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> + <source>Args</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="obsolete">Args</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> + <source>DstIP</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Bestemmings-ip</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> + <source>DstHost</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Bestemmingshost</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> + <source>DstPort</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Bestemmingspoort</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="175"/> + <source>Addr</source> + <translation type="obsolete">Dirección</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="181"/> + <source>Connections</source> + <translation type="obsolete">Conexiones</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="182"/> + <source>Dropped</source> + <translation type="obsolete">Rechazadas</translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="17"/> + <source>What</source> + <translation>Wat</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="931"/> + <source>Apply to</source> + <translation>Toepassen op</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="942"/> + <source>Reject</source> + <translation>Afwijzen</translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="19"/> + <source>Network name</source> + <translation>Netwerknaam</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="378"/> + <source>Addr</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="obsolete">Dirección</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="291"/> + <source>Uptime</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Uptime</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="421"/> + <source>Connections</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Verbindingen</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="422"/> + <source>Dropped</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Afgewezen</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="437"/> + <source>What</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Wat</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> + <source>Precedence</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Voorkomen</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="776"/> + <source>New node connected</source> + <translation>Nieuw knooppunt verbonden</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> + <source>Description</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Beschrijving</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> + <source>Cmdline</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Opdrachtregel</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="406"/> + <source>Export rules</source> + <translation>Regels exporteren</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="407"/> + <source>Import rules</source> + <translation>Regels importeren</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="408"/> + <source>Export events to CSV</source> + <translation>Gebeurtenissen exporteren naar csv-bestand</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> + <source>Quit</source> + <translation>Afsluiten</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="932"/> + <source>Export</source> + <translation>Exporteren</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="964"/> + <source>To clipboard</source> + <translation>Naar klembord</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="965"/> + <source>To disk</source> + <translation>Naar schijf</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2523"/> + <source>Select a directory to export rules</source> + <translation>Kies een exportmap</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1191"/> + <source> Your are about to delete this entry. </source> + <translation> U staat op het punt om dit item te verwijderen. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1678"/> + <source> You are about to delete this node. </source> + <translation> U staat op het punt om dit knooppunt te verwijderen. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1687"/> + <source><b>Error deleting node</b><br><br></source> + <comment>{0}</comment> + <translation><b>Het knooppunt kan niet worden verwijderd</b><br><br></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2478"/> + <source>Error exporting rules</source> + <translation>De regels kunnen niet worden geëxporteerd</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2552"/> + <source>Select a directory with rules to import (JSON files)</source> + <translation>Kies een map met te importeren regels (json-bestanden)</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2566"/> + <source>Rules imported fine</source> + <translation>De regels zijn geïmporteerd</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="211"/> + <source>WARNING</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="833"/> + <source>Details</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="835"/> + <source>New</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>stats_deleterule</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="774"/> + <source> Your are about to delete this rule. </source> + <translation type="obsolete"> Estás a punto de borrar esta regla. </translation> + </message> +</context> +<context> + <name>stats_deleterule2</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="776"/> + <source> Are you sure?</source> + <translation type="obsolete"> ¿Estás seguro?</translation> + </message> +</context> +<context> + <name>stats_disabled</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="74"/> + <source>Disabled</source> + <translation type="obsolete">Deshabilitado</translation> + </message> +</context> +<context> + <name>stats_notrunning</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="73"/> + <source>Not running</source> + <translation type="obsolete">Parado</translation> + </message> +</context> +<context> + <name>stats_running</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="75"/> + <source>Running</source> + <translation type="obsolete">Interceptando</translation> + </message> +</context> +<context> + <name>stats_wintitle</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> + <source>OpenSnitch Network Statistics</source> + <translation type="obsolete">Eventos de red OpenSnitch</translation> + </message> +</context> +<context> + <name>stats_wintitle2</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="411"/> + <source>OpenSnitch Network Statistics for</source> + <translation type="obsolete">Eventos de OpenSnitch de</translation> + </message> +</context> +</TS> diff --git a/ui/i18n/locales/pt_BR/opensnitch-pt_BR.ts b/ui/i18n/locales/pt_BR/opensnitch-pt_BR.ts new file mode 100644 index 0000000..7a7f907 --- /dev/null +++ b/ui/i18n/locales/pt_BR/opensnitch-pt_BR.ts @@ -0,0 +1,3393 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="pt_BR"> +<context> + <name>Dialog</name> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="34"/> + <source>opensnitch-qt</source> + <translation>opensnitch-qt</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="300"/> + <source>User ID</source> + <translation>ID do usuário</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="334"/> + <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source> + <translation><html><head/><body><p><span style=" font-weight:600;">Executado de</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="647"/> + <source>TextLabel</source> + <translation>TextLabel</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="437"/> + <source>Source IP</source> + <translation>IP de origem</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="458"/> + <source>Process ID</source> + <translation>ID de processo</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="601"/> + <source>Destination IP</source> + <translation>IP de destino</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="622"/> + <source>Dst Port</source> + <translation>Porta Dst</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="226"/> + <source>(/path/to/bin/chromium)</source> + <translation type="obsolete">(/caminho/para/bin/chromium)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="271"/> + <source>Chromium Web Browser wants to connect to www.evilsocket.net on tcp port 443. And maybe to www.goodsocket.net on port 344</source> + <translation type="obsolete">O navegador da Web Chromium deseja se conectar a www.evilsocket.net na porta tcp 443. E talvez a www.goodsocket.net na porta 344</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="702"/> + <source>from this executable</source> + <translation>a partir deste executável</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="707"/> + <source>from this command line</source> + <translation>a partir desta linha de comando</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="712"/> + <source>this destination port</source> + <translation>esta porta de destino</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="717"/> + <source>this user</source> + <translation>este usuário</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="722"/> + <source>this destination ip</source> + <translation>este ip de destino</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="751"/> + <source>once</source> + <translation>uma vez</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="756"/> + <source>30s</source> + <translation>30s</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="761"/> + <source>5m</source> + <translation>5m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="766"/> + <source>15m</source> + <translation>15m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="771"/> + <source>30m</source> + <translation>30m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="776"/> + <source>1h</source> + <translation>1h</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="706"/> + <source>for this session</source> + <translation type="obsolete">para esta sessão</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="786"/> + <source>forever</source> + <translation>para sempre</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="346"/> + <source>Deny</source> + <translation>Negar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="337"/> + <source>Allow</source> + <translation>Permitir</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="865"/> + <source>+</source> + <translation>+</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="781"/> + <source>until reboot</source> + <translation>até reiniciar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="727"/> + <source>from this PID</source> + <translation>a partir desse PID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="809"/> + <source>action</source> + <translation>ação</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="14"/> + <source>Firewall</source> + <translation>Firewall</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="55"/> + <source><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></source> + <translation><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="320"/> + <source>Inbound</source> + <translation>De entrada</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="313"/> + <source>Outbound</source> + <translation>De saída</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="275"/> + <source>Profile</source> + <translation>Perfil</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="375"/> + <source>Allow inbound connections to a port</source> + <translation>Permitir conexões de entrada para uma porta</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="378"/> + <source>Allow service (IN)</source> + <translation>Permitir serviço (Entrada)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="398"/> + <source>Exclude outbound connections to a port from being intercepted</source> + <translation>Excluir conexões de saída para uma porta de serem interceptadas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="407"/> + <source>Allow service (OUT)</source> + <translation>Permitir serviço (Saída)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="427"/> + <source>New rule</source> + <translation>Nova regra</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="421"/> + <source>Close</source> + <translation>Fechar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="14"/> + <source>Firewall rule</source> + <translation>Regra do firewall</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="26"/> + <source>Node</source> + <translation>Node</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="38"/> + <source>Enable</source> + <translation>Habilitar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="50"/> + <source>Description</source> + <translation>Descrição</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="90"/> + <source>Simple</source> + <translation>Simple</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="154"/> + <source>Add new condition</source> + <translation>Adicionar nova condição</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="177"/> + <source>Remove selected condition</source> + <translation>Remover condição selecionada</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="221"/> + <source>Direction</source> + <translation>Direção</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="232"/> + <source>IN</source> + <translation>Entrada</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="241"/> + <source>OUT</source> + <translation>Saída</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="268"/> + <source>Action</source> + <translation>Ação</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="279"/> + <source>ACCEPT</source> + <translation>ACEITAR</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="288"/> + <source>DROP</source> + <translation>DERRUBAR</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="297"/> + <source>REJECT</source> + <translation>REJEITAR</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="306"/> + <source>RETURN</source> + <translation>RETORNAR</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="432"/> + <source>Clear</source> + <translation>LImpar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="443"/> + <source>Delete</source> + <translation>Deletar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="454"/> + <source>Save</source> + <translation>Salvar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="465"/> + <source>Add</source> + <translation>Adicionar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="250"/> + <source>FORWARD</source> + <translation>AVANÇAR</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="255"/> + <source>PREROUTING</source> + <translation>PRÉ-ENCAMINHAMENTO</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="260"/> + <source>POSTROUTING</source> + <translation>PÓS-ROTEAMENTO</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="315"/> + <source>QUEUE</source> + <translation>FILA</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="323"/> + <source>DNAT</source> + <translation>DNAT</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="328"/> + <source>SNAT</source> + <translation>SNAT</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="333"/> + <source>REDIRECT</source> + <translation>REDIRECIONAR</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="349"/> + <source>depending on the Action (i.e.: target), the syntaxis of the parameters will vary. +Some examples: + +QUEUE -> num 0 (or 1, 2, ...) +REDIRECT, TPROXY, DNAT, SNAT, MASQUERADE: + to :22 + to 192.168.1.254:8080 + to 192.168.1.254 + to 1024-2048 (masquerade)</source> + <translation>dependendo da Ação (ou seja: alvo), a sintaxe dos parâmetros irá variar. +Alguns exemplos: + +FILA -> num 0 (ou 1, 2, ...) +REDIRECIONAMENTO, TPROXY, DNAT, SNAT, MASCARADO: + para :22 + para 192.168.1.254:8080 + para 192.168.1.254 + para 1024-2048 (mascarado)</translation> + </message> +</context> +<context> + <name>PreferencesDialog</name> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="14"/> + <source>Preferences</source> + <translation>Preferências</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="487"/> + <source>UI</source> + <translation>UI</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="54"/> + <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Este tempo limite é a contagem regressiva que você vê quando uma caixa de diálogo pop-up é exibida.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="469"/> + <source>Default timeout</source> + <translation>Tempo limite padrão</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="340"/> + <source>Pop-up default duration</source> + <translation>Duração padrão do pop-up</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1140"/> + <source>Default duration</source> + <translation>Duração padrão</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="162"/> + <source>Pop-up default action</source> + <translation type="obsolete">Ação padrão de pop-up</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="483"/> + <source>Default action</source> + <translation type="obsolete">Ação padrão</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="323"/> + <source>Default target</source> + <translation>Alvo padrão</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="179"/> + <source>center</source> + <translation>centro</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="184"/> + <source>top right</source> + <translation>superior direito</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="189"/> + <source>bottom right</source> + <translation>inferior direito</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="194"/> + <source>top left</source> + <translation>superior esquerdo</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="199"/> + <source>bottom left</source> + <translation>inferior esquerdo</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="167"/> + <source>Prompt dialog default position on screen</source> + <translation type="obsolete">Posição padrão da caixa de diálogo de prompt na tela</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="283"/> + <source>by executable</source> + <translation>por executável</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="288"/> + <source>by command line</source> + <translation>por linha de comando</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="293"/> + <source>by destination port</source> + <translation>por porta de destino</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="298"/> + <source>by destination ip</source> + <translation>por ip de destino</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="303"/> + <source>by user id</source> + <translation>por id de usuário</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1148"/> + <source>once</source> + <translation>uma vez</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="219"/> + <source>30s</source> + <translation>30s</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="224"/> + <source>5m</source> + <translation>5m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="229"/> + <source>15m</source> + <translation>15m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="234"/> + <source>30m</source> + <translation>30m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="239"/> + <source>1h</source> + <translation>1h</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="240"/> + <source>for this session</source> + <translation type="obsolete">para esta sessão</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="249"/> + <source>forever</source> + <translation>para sempre</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1116"/> + <source>deny</source> + <translation>negar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1125"/> + <source>allow</source> + <translation>permitir</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="411"/> + <source>Disable pop-ups, only display an alert</source> + <translation type="obsolete">Desativar pop-ups, exibir apenas um alerta</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="950"/> + <source>Nodes</source> + <translation>Nodes</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1183"/> + <source>Process monitor method</source> + <translation>Método de monitoramento de processo</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1137"/> + <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source> + <translation><html><head/><body><p>A duração padrão ocorrerá quando não houver interface do usuário conectada.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1077"/> + <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source> + <translation><html><head/><body><p>Endereço do node</p><p>Padrão: unix:///tmp/osui.sock (unix:// é obrigatório se for um soquete Unix)</p><p>Também pode ser um endereço IP com a porta: 127.0.0.1:50051</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1080"/> + <source>Address</source> + <translation>Endereço</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1301"/> + <source>Default log level</source> + <translation>Nível de registro padrão</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1010"/> + <source>Version</source> + <translation>Versão</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1099"/> + <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source> + <translation><html><head/><body><p>A ação padrão ocorrerá quando não houver interface do usuário conectada.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="681"/> + <source>audit</source> + <translation type="obsolete">auditar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1234"/> + <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source> + <translation><html><head/><body><p>Arquivo de log para gravar logs.<br/></p><p>/dev/stdout irá imprimir registros na saída padrão.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1237"/> + <source>Log file</source> + <translation>Arquivo de log</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="788"/> + <source>IMPORTANT</source> + <translation type="obsolete">IMPORTANTE</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="793"/> + <source>WARNING</source> + <translation type="obsolete">ADVERTÊNCIA</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="798"/> + <source>ERROR</source> + <translation type="obsolete">ERRO</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="578"/> + <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons.</p><p>The pop-up dialog will only contain information about the network connection.</p></body></html></source> + <translation type="obsolete">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Se marcado, o opensnitch solicitará que você permita ou negue conexões que não tenham um PID associado, devido a vários motivos.&lt;/p&gt;&lt;p&gt;A caixa de diálogo pop-up conterá apenas informações sobre a conexão de rede.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="581"/> + <source>Intercept Unknown Connections</source> + <translation type="obsolete">Interceptar conexões desconhecidas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="962"/> + <source>HostName</source> + <translation>HostName</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1091"/> + <source>unix:///tmp/osui.sock</source> + <translation>unix:///tmp/osui.sock</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1153"/> + <source>until restart</source> + <translation>até reiniciar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1158"/> + <source>always</source> + <translation>sempre</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1312"/> + <source>/var/log/opensnitchd.log</source> + <translation>/var/log/opensnitchd.log</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1317"/> + <source>/dev/stdout</source> + <translation>/dev/stdout</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1052"/> + <source>Apply configuration to all nodes</source> + <translation>Aplicar configuração a todos os nodes</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1353"/> + <source>Database</source> + <translation>Base de dados</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="630"/> + <source>Database name</source> + <translation type="obsolete">Nome do banco de dados</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1388"/> + <source>In memory</source> + <translation>Na memória</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1393"/> + <source>File</source> + <translation>Arquivo</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="669"/> + <source>/path/to/the/file.db</source> + <translation type="obsolete">/caminho/para/o/arquivo.db</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1696"/> + <source>Close</source> + <translation>Fechar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1707"/> + <source>Apply</source> + <translation>Aplicar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1718"/> + <source>Save</source> + <translation>Salvar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="244"/> + <source>until reboot</source> + <translation>até reiniciar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1407"/> + <source>Database type</source> + <translation>Tipo de banco de dados</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1414"/> + <source>Select</source> + <translation>Selecionar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="83"/> + <source>Configure the</source> + <translation type="obsolete">Configure o</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="83"/> + <source>Pop-ups default options</source> + <translation type="obsolete">Opções padrão de pop-ups</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="367"/> + <source>Pop-ups default position on screen</source> + <translation type="obsolete">Posição padrão dos pop-ups na tela</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="105"/> + <source><html><head/><body><p>The advanced view allows you to apply more filters on a connection</p><p>when a pop-up appears.</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>A visualização avançada permite que você aplique mais filtros em uma conexão</p><p>quando um pop-up aparece.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="359"/> + <source>Show advanced view by default</source> + <translation>Mostrar visualização avançada por padrão</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="815"/> + <source>Action</source> + <translation>Ação</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="375"/> + <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source> + <translation><html><head/><body><p>Se marcado, os pop-ups serão exibidos com a visualização avançada ativa.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="343"/> + <source>Duration</source> + <translation>Duração</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="263"/> + <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source> + <translation><html><head/><body><p>Por padrão, quando um novo pop-up aparece, em sua forma mais simples, você será capaz de filtrar conexões ou aplicativos por uma propriedade da conexão (executável, porta, IP, etc).</p><p>Com essas opções, você pode escolher vários campos para filtrar conexões para.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="266"/> + <source>Filter connections also by:</source> + <translation>Filtre as conexões também por:</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="365"/> + <source>If checked, this field will be checked when a pop-up is displayed</source> + <translation type="obsolete">Se marcado, este campo será verificado quando um pop-up for exibido</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="81"/> + <source>User ID</source> + <translation>ID do usuário</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="97"/> + <source>Destination port</source> + <translation>Porta de destino</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="113"/> + <source>Destination IP</source> + <translation>IP de destino</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="466"/> + <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source> + <translation><html><head/><body><p>Este tempo limite é a contagem regressiva que você vê quando uma caixa de diálogo pop-up é exibida.</p><p>Se o pop-up não for respondido, as opções padrão serão aplicadas.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="356"/> + <source>The advanced view allows you to easily select multiple fields to filter connections</source> + <translation>A visualização avançada permite que você selecione facilmente vários campos para filtrar conexões</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="110"/> + <source>If checked, this field will be selected when a pop-up is displayed</source> + <translation>Se marcado, este campo será selecionado quando um pop-up for exibido</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="159"/> + <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source> + <translation><html><head/><body><p>Ação padrão de pop-up.</p><p>Quando uma nova conexão de saída está prestes a ser estabelecida, esta ação será selecionada por padrão, então se o tempo limite disparar, esta é a opção que será aplicada.</p><p><br/></p><p>Enquanto um pop-up pede ao usuário para permitir ou negar uma conexão:</p><p>1. novas conexões de saída são negadas.</p><p>2. conexões conhecidas são permitidas ou negadas com base nas regras definidas pelo usuário.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1102"/> + <source>Default action when the GUI is disconnected</source> + <translation>Ação padrão quando a GUI é desconectada</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1169"/> + <source>Debug invalid connections</source> + <translation>Depurar conexões inválidas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="39"/> + <source>Pop-ups</source> + <translation>Pop-ups</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="64"/> + <source>Default options</source> + <translation>Opções padrão</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="330"/> + <source>Default position on screen</source> + <translation>Posição padrão na tela</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="896"/> + <source>any temporary rules</source> + <translation>quaisquer regras temporárias</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="487"/> + <source><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Quando esta opção é selecionada, as regras da duração selecionada não serão adicionadas à lista de regras temporárias na GUI.</p><p><br/></p><p>As regras temporárias ainda serão válidas e você pode usá-las quando solicitado a permitir/negar uma nova conexão.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="490"/> + <source>Don't save rules of duration</source> + <translation type="obsolete">Não salve regras de duração</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="751"/> + <source>Time</source> + <translation>Tempo</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="831"/> + <source>Destination</source> + <translation>Destino</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="799"/> + <source>Protocol</source> + <translation>Protocolo</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="847"/> + <source>Process</source> + <translation>Processo</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="767"/> + <source>Rule</source> + <translation>Regra</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="783"/> + <source>Node</source> + <translation>Node</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="723"/> + <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using wireguard.</p></body></html></source> + <translation type="obsolete">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Se marcado, o opensnitch solicitará que você permita ou negue conexões que não tenham um PID asocciado, devido a vários motivos, principalmente devido a conexões de mau estado.&lt;/p&gt;&lt;p&gt;A caixa de diálogo pop-up conterá apenas informações sobre a conexão de rede.&lt;/p&gt;&lt;p&gt;Existem alguns cenários em que essas conexões são válidas, como ao estabelecer uma VPN usando wireguard.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="712"/> + <source>Events tab columns</source> + <translation>Colunas da guia de eventos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="308"/> + <source>by PID</source> + <translation>por PID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1166"/> + <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source> + <translation><html><head/><body><p>Se marcado, o OpenSnitch solicitará que você permita ou negue conexões que não tenham um PID associado, devido a vários motivos, principalmente devido a conexões ruins.</p><p>A caixa de diálogo pop-up conterá apenas informações sobre a conexão de rede.</p><p>Existem alguns cenários em que essas conexões são válidas, como ao estabelecer uma VPN usando o WireGuard.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="476"/> + <source>Disable pop-ups, only display a notification</source> + <translation>Desativar pop-ups, exibir apenas uma notificação</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="616"/> + <source>Desktop notifications</source> + <translation>Notificações da área de trabalho</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="646"/> + <source>Use system notifications</source> + <translation>Usar notificações do sistema</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="662"/> + <source>Use Qt notifications</source> + <translation>Usar notificações do Qt</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="691"/> + <source>Test</source> + <translation>Testar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1501"/> + <source>minutes</source> + <translation>minutos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1533"/> + <source>Minutes between events purges</source> + <translation>Minutos entre expurgos de eventos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1559"/> + <source>days</source> + <translation>dias</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1572"/> + <source>Maximum days of events to keep</source> + <translation>Máximo de dias de eventos para manter</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="147"/> + <source>reject</source> + <translation>rejeitar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="563"/> + <source>System</source> + <translation>Sistema</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="857"/> + <source>Command line</source> + <translation>Linha de comando</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="577"/> + <source>Theme</source> + <translation>Tema</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="875"/> + <source>Rules</source> + <translation>Regras</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="883"/> + <source>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI. + +Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</source> + <translation>Quando esta opção é selecionada, as regras da duração selecionada não serão adicionadas à lista de regras temporárias na GUI. + +As regras temporárias ainda serão válidas e você poderá usá-las quando solicitado a permitir/negar uma nova conexão.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="888"/> + <source>Don't save/Delete rules of duration</source> + <translation>Não salvar/excluir regras de duração</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="906"/> + <source>30s or less</source> + <translation>30s ou menos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="911"/> + <source>5m or less</source> + <translation>5m ou menos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="916"/> + <source>15m or less</source> + <translation>15m ou menos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="921"/> + <source>30m or less</source> + <translation>30m ou menos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="926"/> + <source>1h or less</source> + <translation>1h ou menos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="509"/> + <source>Language</source> + <translation>Idioma</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1071"/> + <source>General</source> + <translation>Geral</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="536"/> + <source>4MiB</source> + <translation>4MiB</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="541"/> + <source>8MiB</source> + <translation>8MiB</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="546"/> + <source>16MiB</source> + <translation>16MiB</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="551"/> + <source>32MiB</source> + <translation>32MiB</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="584"/> + <source>Maximum size for receiving messages from nodes. Default 4MB</source> + <translation>Tamanho máximo para receber mensagens de nós. Padrão 4 MB</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="587"/> + <source>Max gRPC channel size</source> + <translation>Tamanho máximo do canal gRPC</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="594"/> + <source>By default the GUI is started when login</source> + <translation>Por padrão, a GUI é iniciada quando o login é feito</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="597"/> + <source>Autostart the GUI upon login</source> + <translation>Iniciar automaticamente a GUI após o login</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="628"/> + <source>Enable</source> + <translation>Habilitar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1221"/> + <source>Logging</source> + <translation>Registrando</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1244"/> + <source><html><head/><body><p>If checked, OpenSnitch will log timestamp microseconds.</p></body></html></source> + <translation><html><head/><body><p>Se marcado, o OpenSnitch registrará microssegundos de registro de data e hora.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1247"/> + <source>Log timestamp microseconds</source> + <translation>Registrar microssegundos de carimbo de data/hora</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1291"/> + <source><html><head/><body><p>If checked, OpenSnitch will use the UTC timezone for timestamps.</p></body></html></source> + <translation><html><head/><body><p>Se marcado, o OpenSnitch usará o fuso horário UTC para timestamps.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1294"/> + <source>Log UTC timestamps</source> + <translation>Registrar carimbos de data/hora UTC</translation> + </message> +</context> +<context> + <name>ProcessDetailsDialog</name> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="14"/> + <source>Process details</source> + <translation>Detalhes do processo</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="61"/> + <source>loading...</source> + <translation>carregando...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="81"/> + <source>CWD: loading...</source> + <translation>CWD: carregando...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="93"/> + <source>mem stats: loading...</source> + <translation>estatísticas mem: carregando...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="121"/> + <source>Status</source> + <translation>Estado</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="135"/> + <source>Open files</source> + <translation>Abrir arquivos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="149"/> + <source>I/O Statistics</source> + <translation>Estatísticas de I/O</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="163"/> + <source>Memory mapped files</source> + <translation>Arquivos mapeados na memória</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="177"/> + <source>Stack</source> + <translation>Pilha</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="191"/> + <source>Environment variables</source> + <translation>Variáveis de ambiente</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="210"/> + <source>Application pids</source> + <translation>Aplicação pids</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="240"/> + <source>Start or stop monitoring this process</source> + <translation>Inicie ou pare de monitorar este processo</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="256"/> + <source>Close</source> + <translation>Fechar</translation> + </message> +</context> +<context> + <name>RulesDialog</name> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="20"/> + <source>Rule</source> + <translation>Regra</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> + <source>Node</source> + <translation>Node</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> + <source>Apply rule to all nodes</source> + <translation>Aplicar regra a todos os nodes</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="379"/> + <source>From this command line</source> + <translation>Para esta linha de comando</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="466"/> + <source>From this executable</source> + <translation>Para este executável</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="56"/> + <source>Action</source> + <translation>Ação</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="138"/> + <source>/path/to/executable, .*/bin/executable[0-9\.]+$, ...</source> + <translation type="obsolete">/caminho/para/o/executavel, .*/bin/executable[0-9\.]+$, ...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="604"/> + <source>To this IP / Network</source> + <translation>Para este IP / Rede</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="97"/> + <source>once</source> + <translation>uma vez</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="864"/> + <source>30s</source> + <translation type="obsolete">30s</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="869"/> + <source>5m</source> + <translation type="obsolete">5m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="874"/> + <source>15m</source> + <translation type="obsolete">15m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="879"/> + <source>30m</source> + <translation type="obsolete">30m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="884"/> + <source>1h</source> + <translation type="obsolete">1h</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="230"/> + <source>until restart</source> + <translation type="obsolete">até reiniciar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="132"/> + <source>always</source> + <translation>sempre</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="896"/> + <source>To this port</source> + <translation>Para esta porta</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="372"/> + <source>From this user ID</source> + <translation>Para este ID de usuário</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="586"/> + <source>Commas or spaces are not allowed to specify multiple domains. + +Use regular expressions instead: +.*(opensnitch|duckduckgo).com +.*\.google.com + +or a single domain: +www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... +gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source> + <translation>Vírgulas ou espaços não podem especificar vários domínios. + +Em vez disso, use expressões regulares: +.*(opensnitch|duckduckgo).com +.*\.google.com + +ou um único domínio: +www.gnu.org - só vai filtrar www.gnu.org, não filtrará ftp.gnu.org, nem www2.gnu.org, ... +gnu.org - só vai filtrar gnu.org, não filtrará www.gnu.org, nem ftp.gnu.org, ...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="597"/> + <source>www.domain.org, .*\.domain.org</source> + <translation>www.dominio.org, .*\.dominio.org</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="520"/> + <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source> + <translation><html><head/><body><p>Apenas TCP, UDP ou UDPLITE são permitidos</p><p>Você pode usar expressão regulares, ou seja: ^(TCP|UDP)$</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="526"/> + <source>TCP</source> + <translation>TCP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="255"/> + <source>UDP</source> + <translation type="obsolete">UDP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="260"/> + <source>UDPLITE</source> + <translation type="obsolete">UDPLITE</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="265"/> + <source>TCP6</source> + <translation type="obsolete">TCP6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="270"/> + <source>UDP6</source> + <translation type="obsolete">UDP6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="275"/> + <source>UDPLITE6</source> + <translation type="obsolete">UDPLITE6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="754"/> + <source>You can specify a single IP: +- 192.168.1.1 + +or a regular expression: +- 192\.168\.1\.[0-9]+ + +multiple IPs: +- ^(192\.168\.1\.1|172\.16\.0\.1)$ + +You can also specify a subnet: +- 192.168.1.0/24 + +Note: Commas or spaces are not allowed to separate IPs or networks.</source> + <translation>Você pode especificar um único IP: +- 192.168.1.1 + +ou uma expressão regular: +- 192\.168\.1\.[0-9]+ + +vários IPs: +- ^(192\.168\.1\.1|172\.16\.0\.1)$ + +Você também pode especificar uma sub-rede: +- 192.168.1.0/24 + +Nota: Vírgulas ou espaços não são permitidos para separar IPs ou redes.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="659"/> + <source>LAN</source> + <translation>LAN</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="669"/> + <source>127.0.0.0/8</source> + <translation>127.0.0.0/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="674"/> + <source>192.168.0.0/24</source> + <translation>192.168.0.0/24</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="679"/> + <source>192.168.1.0/24</source> + <translation>192.168.1.0/24</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="684"/> + <source>192.168.2.0/24</source> + <translation>192.168.2.0/24</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="689"/> + <source>192.168.0.0/16</source> + <translation>192.168.0.0/16</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="694"/> + <source>169.254.0.0/16</source> + <translation>169.254.0.0/16</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="699"/> + <source>172.16.0.0/12</source> + <translation>172.16.0.0/12</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="704"/> + <source>10.0.0.0/8</source> + <translation>10.0.0.0/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="709"/> + <source>::1/128</source> + <translation>::1/128</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="714"/> + <source>fc00::/7</source> + <translation>fc00::/7</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="719"/> + <source>ff00::/8</source> + <translation>ff00::/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="724"/> + <source>fe80::/10</source> + <translation>fe80::/10</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="729"/> + <source>fd00::/8</source> + <translation>fd00::/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="89"/> + <source>Duration</source> + <translation>Duração</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="627"/> + <source>Protocol</source> + <translation>Protocolo</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="744"/> + <source>To this host</source> + <translation>Para este host</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="151"/> + <source>Deny</source> + <translation>Negar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="191"/> + <source>Allow</source> + <translation>Permitir</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="245"/> + <source>Name</source> + <translation>Nome</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="207"/> + <source>Enable</source> + <translation>Habilitar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="238"/> + <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them. + +000-allow-localhost +001-deny-broadcast +...</source> + <translation>As regras são verificadas em ordem alfabética, para que você possa nomeá-las de acordo para priorizá-las. + +000-permitir-localhost +001-negar-transmissão +...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="773"/> + <source>leave blank to autocreate</source> + <translation type="obsolete">deixe em branco para criar automaticamente</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="214"/> + <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one. + +You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example: + +[x] Priority - 000-priority-rule +[ ] Priority - 001-less-priority-rule</source> + <translation>Se marcada, esta regra terá precedência sobre o resto das regras. Nenhuma outra regra será verificada após esta. + +Você deve nomear a regra de forma que ela seja verificada primeiro, porque eles são verificados em ordem alfabética. Por exemplo: + +[x] Prioridade - 000-regra-prioritaria +[ ] Prioridade - 001-regra-menos-prioritaria</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="222"/> + <source>Priority rule</source> + <translation>Regra de prioridade</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1086"/> + <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source> + <translation><html><head/><body><p>Por padrão, o campo das regras não diferencia maiúsculas de minúsculas, ou seja, se um processo tentar acessar gOOgle.CoM e você tiver uma regra para Negar. *Google.com, a conexão será bloqueada.<br/></p><p>Se você marcar esta caixa, deverá especificar a string exata (domínio, executável, linha de comando) que deseja filtrar.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1089"/> + <source>Case-sensitive</source> + <translation>Sensível a maiúsculas e minúsculas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="686"/> + <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Você pode especificar várias portas usando expressões regulares:</p><p><br/></p><p>- 53, 80 o 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 o 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="127"/> + <source>until reboot</source> + <translation>até reiniciar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="974"/> + <source>To this list of domains</source> + <translation>Para esta lista de domínios</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="539"/> + <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Selecione um diretório com listas de domínios para bloquear ou permitir.</p><p>Coloque dentro desse diretório arquivos com qualquer extensão que contenha listas de domínios.</p><p><br/>O formato de cada entrada de uma lista é o seguinte (formato de hosts):</p><p>127.0.0.1 www.dominio.com</p><p>ou </p><p>0.0.0.0 www.dominio.com</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="346"/> + <source>Applications</source> + <translation>Aplicativos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="216"/> + <source><html><head/><body><p>This field will only match the executable path. It is not modifiable by the user.<br/></p><p>You can use regular expressions to deny executions from /tmp for example:<br/></p><p>^/tmp/.*$</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Este campo irá corresponder apenas ao caminho do executável. Não é modificável pelo usuário.<br/></p><p>Você pode usar expressões regulares para negar execuções de /tmp, por exemplo:<br/></p><p>^/tmp/.*$</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="389"/> + <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source> + <translation><html><head/><body><p>Este campo irá conter e corresponder à linha de comando que foi executada pelo usuário.<br/></p><p>Se o usuário digitou o comando, apenas o comando aparecerá:</p><p>telnet 1.2.3.4<br/></p><p>Se o usuário digitou o caminho absoluto ou relativo para o comando, é isso que aparecerá:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="399"/> + <source>From this PID</source> + <translation>A partir deste PID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="485"/> + <source>Network</source> + <translation>Rede</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="926"/> + <source>List of domains/IPs</source> + <translation>Lista de domínios/IPs</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="932"/> + <source>To this list of network ranges</source> + <translation>Para esta lista de intervalos de rede</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="939"/> + <source>To this list of IPs</source> + <translation>Para esta lista de IPs</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="965"/> + <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation><html><head/><body><p>Selecione um diretório com arquivos contendo uma lista de IPs para bloquear ou permitir:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>Um IP por linha. Linhas vazias ou iniciadas com # são ignoradas.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1000"/> + <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation><html><head/><body><p>Selecione um diretório com arquivos contendo uma lista de intervalos de rede para bloquear ou permitir:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>Um intervalo de rede por linha. Linhas vazias ou iniciadas com # são ignoradas.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1028"/> + <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source> + <translation><html><head/><body><p>Selecione um diretório com listas de domínios para bloquear ou permitir.</p><p>Coloque dentro desse diretório arquivos com qualquer extensão contendo listas de domínios.</p><p><br/>O formato de cada entrada de uma lista é o seguinte (formato de hosts):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Linhas vazias ou iniciadas com # são ignoradas.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1043"/> + <source>To this list of domains +(regular expressions)</source> + <translation>Para esta lista de domínios +(expressões regulares)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1070"/> + <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation><html><head/><body><p>Selecione um diretório com arquivos contendo expressões regulares de domínios para bloquear ou permitir:</p><p>.*\.example\.com</p><p>Você também pode usar um domínio como: &quot;example.com&quot; , e vai combinar whatever.example.com, whatever.example.com.localdomain, etc.</p><p>Um domínio por linha. Linhas vazias ou iniciadas com # são ignoradas.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="168"/> + <source>Reject</source> + <translation>Rejeitar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1145"/> + <source>Description...</source> + <translation>Descrição...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="355"/> + <source><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></source> + <translation><html><head/><body><p>O valor deste campo é sempre o caminho absoluto para o executável: /caminho/do/binário<br/></p><p>Exemplos:</p><p>- Simples: /caminho/do/binário</p><p>- Vários caminhos: ^/usr/lib(64|)/firefox/firefox$</p><p>- Vários binários: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Negar/Permitir execuções a partir de /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>Para mais exemplos, visite a <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">página wiki</a> ou pergunte nos <a href="https://github.com/evilsocket/opensnitch/discussions">fóruns de discussão</a>.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="365"/> + <source>Is regular expression</source> + <translation>É expressão regular</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="473"/> + <source>is regular expression</source> + <translation>é expressão regular</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="857"/> + <source>Network interface</source> + <translation>Interface de rede</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1080"/> + <source>More</source> + <translation>Mais</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1096"/> + <source>Don't log connections that match this rule</source> + <translation>Não registre conexões que correspondam a esta regra</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1099"/> + <source>Don't log connections</source> + <translation>Não registre conexões</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="148"/> + <source>Deny will just discard the connection</source> + <translation>Negar apenas descartará a conexão</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="165"/> + <source>Reject will drop the connection, and kill the socket that initiated it</source> + <translation>Rejeitar derrubará a conexão e matará o soquete que a iniciou</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="185"/> + <source>Allow will allow the connection</source> + <translation>Permitir permitirá a conexão</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="560"/> + <source>ICMP</source> + <translation>ICMP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="565"/> + <source>ICMP6</source> + <translation>ICMP6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="570"/> + <source>SCTP</source> + <translation>SCTP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="575"/> + <source>SCTP6</source> + <translation>SCTP6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="737"/> + <source>From this IP / Network</source> + <translation>A partir deste IP / Rede</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="866"/> + <source>From this port</source> + <translation>A partir desta porta</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="912"/> + <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> + <translation><html><head/><body><p>Você pode especificar várias portas usando expressões regulares:</p><p>- 53, 80 ou 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="664"/> + <source>MULTICAST</source> + <translation>MULTICAST</translation> + </message> +</context> +<context> + <name>StatsDialog</name> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="34"/> + <source>OpenSnitch Network Statistics</source> + <translation>Estatísticas da rede OpenSnitch</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="287"/> + <source>Save to CSV</source> + <translation type="obsolete">Salvar em CSV.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="297"/> + <source>Ctrl+S</source> + <translation type="obsolete">Ctrl+S</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="333"/> + <source>Create a new rule</source> + <translation>Crie uma nova regra</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="376"/> + <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source> + <translation><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="413"/> + <source>Status</source> + <translation>Estado</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1814"/> + <source>-</source> + <translation>-</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="451"/> + <source>Start or Stop interception</source> + <translation>Iniciar ou parar a interceptação</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="496"/> + <source>Events</source> + <translation>Eventos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="94"/> + <source>Filter</source> + <translation>Filtro</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="107"/> + <source>Allow</source> + <translation>Permitir</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="116"/> + <source>Deny</source> + <translation>Negar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="143"/> + <source>Ex.: firefox</source> + <translation>Ex.: firefox</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="205"/> + <source>50</source> + <translation>50</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="210"/> + <source>100</source> + <translation>100</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="215"/> + <source>200</source> + <translation>200</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="220"/> + <source>300</source> + <translation>300</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="829"/> + <source>Nodes</source> + <translation>Nodes</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="554"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Addr column to view details of a node)</span></p></body></html></source> + <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(clique duas vezes na coluna endereço para ver os detalhes de um node)</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1721"/> + <source>Rules</source> + <translation>Regras</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="998"/> + <source>enable</source> + <translation>habilitar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="674"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Name column to view details of a rule)</span></p></body></html></source> + <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(clique duas vezes na coluna Nome para ver os detalhes de uma regra)</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="692"/> + <source>search rule name</source> + <translation type="obsolete">nome da regra de pesquisa</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="785"/> + <source>Application rules</source> + <translation>Regras de aplicação</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="939"/> + <source>Permanent</source> + <translation>Permanente</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="948"/> + <source>Temporary</source> + <translation>Temporário</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1069"/> + <source>Hosts</source> + <translation>Hosts</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1364"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click to view details of an item)</span></p></body></html></source> + <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(clique duas vezes para ver os detalhes de um item)</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1162"/> + <source>Applications</source> + <translation>Aplicativos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1278"/> + <source>Addresses</source> + <translation>Endereços</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1371"/> + <source>Ports</source> + <translation>Portas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1458"/> + <source>Users</source> + <translation>Usuários</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1565"/> + <source>Connections</source> + <translation>Conexões</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1617"/> + <source>Dropped</source> + <translation>Dropado</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1669"/> + <source>Uptime</source> + <translation>Tempo de atividade</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1788"/> + <source>Version</source> + <translation>Versão</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="233"/> + <source>Delete all intercepted events</source> + <translation>Excluir todos os eventos interceptados</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1028"/> + <source>Edit rule</source> + <translation>Editar regra</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1045"/> + <source>Delete rule</source> + <translation>Excluir regra</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="926"/> + <source>Delete all intercepted hosts</source> + <translation type="obsolete">Excluir todos os hosts interceptadas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1051"/> + <source>Delete all intercepted applications</source> + <translation type="obsolete">Excluir todos os aplicativos interceptadas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1159"/> + <source>Delete all intercepted addresses</source> + <translation type="obsolete">Excluir todos os endereços interceptados</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1261"/> + <source>Delete all intercepted ports</source> + <translation type="obsolete">Excluir todas as portas interceptadas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1371"/> + <source>Delete all intercepted users</source> + <translation type="obsolete">Excluir todos os usuários interceptados</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="699"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click on a row to view details of a rule)</span></p></body></html></source> + <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(clique duas vezes em uma linha para ver os detalhes de uma regra)</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="915"/> + <source>Delete connections that matched this rule</source> + <translation type="obsolete">Exclua conexões que correspondam a esta regra</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="930"/> + <source>All applications</source> + <translation>Todos os aplicativos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="125"/> + <source>Reject</source> + <translation>Rejeitar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="180"/> + <source>0</source> + <translation>0</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="780"/> + <source>2</source> + <translation>2</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="957"/> + <source>System rules</source> + <translation>Regras do sistema</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="640"/> + <source>Delete this node</source> + <translation>Excluir este node</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="656"/> + <source>Show the preferences of this node</source> + <translation>Mostrar as preferências deste node</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="672"/> + <source>Start or stop interception of this node</source> + <translation>Iniciar ou parar a interceptação deste node</translation> + </message> +</context> +<context> + <name>contextual_menu</name> + <message> + <location filename="../../../opensnitch/service.py" line="48"/> + <source>Statistics</source> + <translation>Estatísticas</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="51"/> + <source>Help</source> + <translation>Ajuda</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="52"/> + <source>Close</source> + <translation>Fechar</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="49"/> + <source>Enable</source> + <translation>Habilitar</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="50"/> + <source>Disable</source> + <translation>Desabilitar</translation> + </message> +</context> +<context> + <name>firewall</name> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="96"/> + <source>Configuration applied.</source> + <translation>Configuração aplicada.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="405"/> + <source>Error: {0}</source> + <translation>Error: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="198"/> + <source>Applying changes...</source> + <translation>Aplicando alterações...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="235"/> + <source>Error getting INPUT chain policy</source> + <translation>Erro ao obter a política da cadeia de ENTRADA</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="242"/> + <source>Error getting OUTPUT chain policy</source> + <translation>Erro ao obter a política de cadeia de SAÍDA</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="295"/> + <source>In order to configure firewall rules from the GUI, we need to use 'nftables' instead of 'iptables'</source> + <translation>Para configurar regras de firewall a partir da GUI, precisamos usar 'nftables' em vez de 'iptables'</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="309"/> + <source>Enabling firewall...</source> + <translation>Habilitando firewall...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="311"/> + <source>Disabling firewall...</source> + <translation>Desabilitando firewall...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="71"/> + <source>Dest Port</source> + <translation>Porta de destino</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="72"/> + <source>Source Port</source> + <translation>Porta de origem</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="73"/> + <source>Dest IP</source> + <translation>IP de destino</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="74"/> + <source>Source IP</source> + <translation>IP de origem</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="75"/> + <source>Input interface</source> + <translation>Interface de entrada</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="76"/> + <source>Output interface</source> + <translation>Interface de saída</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="77"/> + <source>Set conntrack mark</source> + <translation>Definir marca de controle</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="78"/> + <source>Match conntrack mark</source> + <translation>Correspondência de marca de controle</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="79"/> + <source>Match conntrack state(s)</source> + <translation>Corresponde ao(s) estado(s) de controle de correspondência</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="80"/> + <source>Set mark on packet</source> + <translation>Definir marca no pacote</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="81"/> + <source>Match packet information</source> + <translation>Correspondência de informações do pacote</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="87"/> + <source>Bandwidth quotas</source> + <translation>Cotas de largura de banda</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="89"/> + <source>Rate limit connections</source> + <translation>Limite de taxa de conexões</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="374"/> + <source>Your protobuf version is incompatible, you need to install protobuf 3.8.0 or superior +(pip3 install --ignore-installed protobuf==3.8.0)</source> + <translation>Sua versão do protobuf é incompatível, você precisa instalar o protobuf 3.8.0 ou superior +(pip3 install --ignore-installed protobuf==3.8.0)</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="398"/> + <source>Rule deleted</source> + <translation>Regra deletada</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="402"/> + <source>Rule added</source> + <translation>Regra adicionada</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="424"/> + <source>You can use ',' or '-' to specify multiple ports/IPs or ranges/values:<br><br>ports: 22 or 22,443 or 50000-60000<br>IPs: 192.168.1.1 or 192.168.1.30-192.168.1.130<br>Values: echo-reply,echo-request<br>Values: new,established,related</source> + <translation>Você pode usar ',' ou '-' para especificar várias portas/IPs ou intervalos/valores:<br><br>ports: 22 ou 22,443 ou 50000-60000<br>IPs: 192.168.1.1 ou 192.168.1.30-192.168.1.130<br>Valores: eco-resposta,eco-pedido<br>Valores: novo,estabelecido,relacionado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="444"/> + <source>Deleting rule, wait</source> + <translation>Excluindo regra, aguarde</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="447"/> + <source>Error updating rule</source> + <translation>Erro ao atualizar regra</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="493"/> + <source>Adding rule, wait</source> + <translation>Adicionando regra, aguarde</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="502"/> + <source><select a statement></source> + <translation><selecione uma declaração></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="803"/> + <source>Equal</source> + <translation>Igual</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="804"/> + <source>Not equal</source> + <translation>Não igual</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="805"/> + <source>Greater or equal than</source> + <translation>Maior ou igual a</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="806"/> + <source>Greater than</source> + <translation>Maior que</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="807"/> + <source>Less or equal than</source> + <translation>Menor ou igual a</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="808"/> + <source>Less than</source> + <translation>Menor que</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1491"/> + <source>Firewall rule</source> + <translation>Regra do firewall</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1028"/> + <source>Simple</source> + <translation>Simple</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1033"/> + <source>Advanced</source> + <translation>Avançado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1154"/> + <source>This rule is not supported yet.</source> + <translation>Esta regra ainda não é suportada.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1219"/> + <source>Exclude service</source> + <translation>Excluir serviço</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1231"/> + <source>Allow inbound connections to the selected port.</source> + <translation>Permitir conexões de entrada para a porta selecionada.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1233"/> + <source>Allow outbound connections to the selected port.</source> + <translation>Permitir conexões de saída para a porta selecionada.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1309"/> + <source>select a statement.</source> + <translation>selecione uma declaração.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1325"/> + <source>value cannot be 0 or empty.</source> + <translation>o valor não pode ser 0 ou vazio.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1337"/> + <source>the value format is 1024/kbytes (or bytes, mbytes, gbytes)</source> + <translation>o formato do valor é 1024/kbytes (ou bytes, mbytes, gbytes)</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1351"/> + <source>the value format is 1024/kbytes/second (or bytes, mbytes, gbytes)</source> + <translation>o formato do valor é 1024/kbytes/segundo (ou bytes, mbytes, gbytes)</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1354"/> + <source>rate-limit not valid, use: bytes, kbytes, mbytes or gbytes.</source> + <translation>limite de taxa inválido, use: bytes, kbytes, mbytes ou gbytes.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1356"/> + <source>time-limit not valid, use: second, minute, hour or day</source> + <translation>limite de tempo inválido, use: segundo, minuto, hora ou dia</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1423"/> + <source>port not valid.</source> + <translation>porta inválida.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="108"/> + <source> +Supported formats: + + - Simple: 23 + - Ranges: 80-1024 + - Multiple ports: 80,443,8080 +</source> + <translation> +Formatos suportados: + + - Simple: 23 + - Gamas: 80-1024 + - Múltiplas portas: 80,443,8080 +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="134"/> + <source> +Supported formats: + + - Simple: 1.2.3.4 + - IP ranges: 1.2.3.100-1.2.3.200 + - Network ranges: 1.2.3.4/24 +</source> + <translation> +Formatos suportados: + + - Simple: 1.2.3.4 + - Intervalos de IP: 1.2.3.100-1.2.3.200 + - Intervalos de rede: 1.2.3.4/24 +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="147"/> + <source>Match input interface. Regular expressions not allowed.</source> + <translation>Interface de entrada de correspondência. Expressões regulares não permitidas.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="154"/> + <source>Match output interface. Regular expressions not allowed.</source> + <translation>Interface de saída de correspondência. Expressões regulares não permitidas.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="161"/> + <source>Set a conntrack mark on the connection, in decimal format.</source> + <translation>Defina uma marca de controle na conexão, no formato decimal.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="171"/> + <source>Match a conntrack mark of the connection, in decimal format.</source> + <translation>Corresponde a uma marca de controle da conexão, no formato decimal.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="178"/> + <source>Match conntrack states. + +Supported formats: + - Simple: new + - Multiple states separated by commas: related,new +</source> + <translation>Corresponde aos estados de controle. + +Formatos suportados: + - Simple: novo + - Vários estados separados por vírgulas: relacionado,novo +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="193"/> + <source> +Match packet's metainformation. + +Value must be in decimal format, except for the "l4proto" option. +For l4proto it can be a lower case string, for example: + tcp + udp + icmp, + etc + +If the value is decimal for protocol or lproto, it'll use it as the code of +that protocol. +</source> + <translation> +Corresponde às metainformações do pacote. + +O valor deve estar no formato decimal, exceto na opção "l4proto". +Para l4proto pode ser uma string em letras minúsculas, por exemplo: + tcp + udp + icmp, + etc + +Se o valor for decimal para protocolo ou lproto, ele o usará como código + desse protocolo. +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="213"/> + <source>Set a mark on the packet matching the specified conditions. The value is in decimal format.</source> + <translation>Defina uma marca no pacote que corresponda às condições especificadas. O valor está no formato decimal.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="221"/> + <source> +Match ICMP codes. + +Supported formats: + - Simple: echo-request + - Multiple separated by commas: echo-request,echo-reply +</source> + <translation> +Corresponde aos códigos ICMP. + +Formatos suportados: + - Simple: eco-pedido + - Múltiplos separados por vírgulas: eco-pedido, eco-resposta +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="234"/> + <source> +Match ICMPv6 codes. + +Supported formats: + - Simple: echo-request + - Multiple separated by commas: echo-request,echo-reply +</source> + <translation> +Corresponde aos códigos ICMPv6. + +Formatos suportados: + - Simple: Formatos suportados + - Múltiplos separados por vírgulas: eco-pedido, eco-resposta +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="247"/> + <source>Print a message when this rule matches a packet.</source> + <translation>Imprima uma mensagem quando esta regra corresponder a um pacote.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="254"/> + <source> +Apply quotas on connections. + +For example when: + - "quota over 10/mbytes" -> apply the Action defined (DROP) + - "quota until 10/mbytes" -> apply the Action defined (ACCEPT) + +The value must be in the format: VALUE/UNITS, for example: + - 10mbytes, 1/gbytes, etc +</source> + <translation> +Aplicar cotas em conexões. + +Por exemplo quando: + - "cota acima de 10/mbytes" -> aplicar a Ação definida (DERRUBAR) + - "cota até 10/mbytes" -> aplicar a Ação definida (ACEITAR) + +O valor deve estar no formato: VALOR/UNIDADES, por exemplo: + - 10mbytes, 1/gbytes, etc +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="286"/> + <source> +Apply limits on connections. + +For example when: + - "limit over 10/mbytes/minute" -> apply the Action defined (DROP, ACCEPT, etc) + (When there're more than 10MB per minute, apply an Action) + + - "limit until 10/mbytes/hour" -> apply the Action defined (ACCEPT) + +The value must be in the format: VALUE/UNITS/TIME, for example: + - 10/mbytes/minute, 1/gbytes/hour, etc +</source> + <translation> +Aplicar limites nas conexões. + +Por exemplo quando: + - "limite acima de 10/mbytes/minuto" -> aplique a Ação definida (DERRUBAR, ACEITAR, etc) + (Quando houver mais de 10MB por minuto, aplique uma Ação) + + - "limite até 10/mbytes/hora" -> aplique a Ação definida (ACEITAR) + +O valor deve estar no formato: VALOR/UNIDADES/TEMPO, por exemplo: + - 10/mbytes/minuto, 1/gbytes/hora, etc +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="623"/> + <source>num</source> + <translation>num</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="637"/> + <source>to</source> + <translation>para</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="482"/> + <source>Add at least one statement.</source> + <translation>Adicione pelo menos uma instrução.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="976"/> + <source>Warning: ct set mark value is empty, malformed rule?</source> + <translation>Aviso: o valor da marca do conjunto ct está vazio, regra malformada?</translation> + </message> +</context> +<context> + <name>messages</name> + <message> + <location filename="../../../opensnitch/service.py" line="299"/> + <source>Info</source> + <translation>Informações</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="303"/> + <source>Error</source> + <translation>Erro</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="307"/> + <source>Warning</source> + <translation>Aviso</translation> + </message> +</context> +<context> + <name>notifications</name> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="684"/> + <source>System notifications are not available, you need to install python3-notify2.</source> + <translation>As notificações do sistema não estão disponíveis, você precisa instalar o python3-notify2.</translation> + </message> +</context> +<context> + <name>popups</name> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="50"/> + <source>until reboot</source> + <translation>até reiniciar</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/> + <source>forever</source> + <translation>para sempre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="115"/> + <source>Allow</source> + <translation>Permitir</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="114"/> + <source>Deny</source> + <translation>Negar</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="208"/> + <source>Unknown process</source> + <translation type="obsolete">Processo desconhecido</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="331"/> + <source>Outgoing connection</source> + <translation>Conexão de saída</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="336"/> + <source>Process launched from:</source> + <translation>Processo lançado de:</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="373"/> + <source>from this executable</source> + <translation>a partir deste executável</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="377"/> + <source>from this command line</source> + <translation>a partir desta linha de comando</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="379"/> + <source>to port {0}</source> + <translation>para a porta {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="442"/> + <source>to {0}</source> + <translation>para {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="382"/> + <source>from user {0}</source> + <translation>do usuário {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="399"/> + <source>to {0}.*</source> + <translation>para {0}.*</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="452"/> + <source>to *.{0}</source> + <translation>para *.{0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="378"/> + <source>to *{0}</source> + <translation type="obsolete">para *{0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="486"/> + <source><b>Remote</b> process %s running on <b>%s</b></source> + <translation><b>Processo remoto</b> %s rodando em <b>%s</b></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="490"/> + <source>is connecting to <b>%s</b> on %s port %d</source> + <translation>está conectando a <b>%s</b> em %s na porta %d</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="502"/> + <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source> + <translation>está tentando resolver <b>%s</b> via %s, %s porta %d</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="386"/> + <source>from this PID</source> + <translation>a partir desse PID</translation> + </message> + <message> + <location filename="../../../opensnitch/notifications.py" line="114"/> + <source>New outgoing connection</source> + <translation>Nova conexão de saída</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="116"/> + <source>Reject</source> + <translation>Rejeitar</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="497"/> + <source>is connecting to <b>%s</b>, %s</source> + <translation>está se conectando a <b>%s</b>, %s</translation> + </message> + <message> + <location filename="../../../opensnitch/notifications.py" line="42"/> + <source>Open</source> + <translation>Abrir</translation> + </message> +</context> +<context> + <name>preferences</name> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="311"/> + <source>Server address can not be empty</source> + <translation>O endereço do servidor não pode estar vazio</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="598"/> + <source>Configuration applied.</source> + <translation>Configuração aplicada.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="409"/> + <source>Exception saving config: {0}</source> + <translation>Configuração de salvamento de exceção: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="529"/> + <source>Applying configuration on {0} ...</source> + <translation>Aplicando configuração em {0} ...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="342"/> + <source>Error loading {0} configuration</source> + <translation>Erro ao carregar configuração de {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="600"/> + <source>Error applying configuration: {0}</source> + <translation>Erro ao aplicar configuração: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="433"/> + <source>Warning</source> + <translation>Aviso</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="433"/> + <source>You must select a file for the database<br>or choose "In memory" type.</source> + <translation>Você deve selecionar um arquivo para o banco de dados&lt;br&gt;ou escolher o tipo &quot;Na memória&quot;.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="424"/> + <source>DB type changed</source> + <translation>Tipo de banco de dados alterado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="38"/> + <source>Restart the GUI in order effects to take effect</source> + <translation type="obsolete">Reinicie a GUI para que os efeitos tenham efeito</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="637"/> + <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source> + <translation>Passe o mouse sobre os textos para exibir a ajuda&lt;br&gt;&lt;br&gt;Não se esqueça de visitar a wiki: &lt;a href=&quot;{0}&quot;&gt;{0}&lt;/a&gt;</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="495"/> + <source>System</source> + <translation>Sistema</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="191"/> + <source>Themes not available. Install qt-material: pip3 install qt-material</source> + <translation>Temas não disponíveis. Instale qt-material: pip3 install qt-material</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="496"/> + <source>UI theme changed</source> + <translation>Tema da IU alterado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="496"/> + <source>Restart the GUI in order to apply the new theme</source> + <translation>Reinicie a GUI para aplicar o novo tema</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="537"/> + <source>Ok</source> + <translation>Ok</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="39"/> + <source>Restart the GUI in order changes to take effect</source> + <translation>Reinicie a GUI para que as mudanças tenham efeito</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="411"/> + <source>There're no nodes connected</source> + <translation>Não há nodes conectados</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="550"/> + <source>Exception saving node config {0}: {1}</source> + <translation>Exceção ao salvar a configuração do node {0}: {1}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="168"/> + <source>System default</source> + <translation>Sistema padrão</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="462"/> + <source>Language changed</source> + <translation>Idioma alterado</translation> + </message> +</context> +<context> + <name>proc_details</name> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="100"/> + <source><b>Error loading process information:</b> <br><br> + +</source> + <translation><b>Erro ao carregar as informações do processo:</b> <br><br> + +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="119"/> + <source><b>Error stopping monitoring process:</b><br><br></source> + <translation><b>Erro ao parar o processo de monitoramento:</b><br><br></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="159"/> + <source>loading...</source> + <translation>carregando...</translation> + </message> +</context> +<context> + <name>rules</name> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="235"/> + <source>There're no nodes connected.</source> + <translation>Não há nodes conectados.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="278"/> + <source>Rule applied.</source> + <translation>Regra aplicada.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="648"/> + <source>protocol can not be empty, or uncheck it</source> + <translation>protocolo não pode estar vazio ou desmarque-o</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="662"/> + <source>Protocol regexp error</source> + <translation>Erro de expressão de protocolo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="666"/> + <source>process path can not be empty</source> + <translation>o caminho do processo não pode estar vazio</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="680"/> + <source>Process path regexp error</source> + <translation>Erro de expressão regular do caminho do processo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="684"/> + <source>command line can not be empty</source> + <translation>a linha de comando não pode estar vazia</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="698"/> + <source>Command line regexp error</source> + <translation>Erro de expressão regular da linha de comando</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="738"/> + <source>Dest port can not be empty</source> + <translation>A porta de destino não pode estar vazia</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="752"/> + <source>Dst port regexp error</source> + <translation>Erro de expressão regular da porta Dst</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="756"/> + <source>Dest host can not be empty</source> + <translation>Dest host não pode estar vazio</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="770"/> + <source>Dst host regexp error</source> + <translation>Erro de expressão regular do host Dst</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="812"/> + <source>Dest IP/Network can not be empty</source> + <translation>O IP/rede de destino não pode estar vazio</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="838"/> + <source>Dst IP regexp error</source> + <translation>Erro de expressão regular de IP Dst</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="850"/> + <source>User ID can not be empty</source> + <translation>O ID do usuário não pode estar vazio</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="864"/> + <source>User ID regexp error</source> + <translation>Erro de expressão regular do ID do usuário</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="280"/> + <source>Error applying rule: {0}</source> + <translation>Erro ao aplicar regra: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="983"/> + <source><b>Rule not supported</b></source> + <translation><b>Regra não suportada</b></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="938"/> + <source>Lists field cannot be empty</source> + <translation>O campo de listas não pode estar vazio</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="940"/> + <source>Lists field must be a directory</source> + <translation>O campo de listas deve ser um diretório</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="546"/> + <source><b>Error loading rule</b></source> + <translation><b>Erro ao carregar regra</b></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="252"/> + <source>There's already a rule with this name.</source> + <translation>Já existe uma regra com este nome.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="868"/> + <source>PID field can not be empty</source> + <translation>O campo PID não pode ficar vazio</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="882"/> + <source>PID field regexp error</source> + <translation>Erro de expressão regular do campo PID</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="970"/> + <source>Select at least one field.</source> + <translation>Selecione pelo menos um campo.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="702"/> + <source>Network interface can not be empty</source> + <translation>A interface de rede não pode estar vazia</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="716"/> + <source>Network interface regexp error</source> + <translation>Erro de expressão regular da interface de rede</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="720"/> + <source>Source port can not be empty</source> + <translation>A porta de origem não pode estar vazia</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="734"/> + <source>Source port regexp error</source> + <translation>Erro de regexp da porta de origem</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="774"/> + <source>Source IP/Network can not be empty</source> + <translation>O IP/rede de origem não pode estar vazio</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="800"/> + <source>Source IP regexp error</source> + <translation>Erro de regexp do IP de origem</translation> + </message> +</context> +<context> + <name>stats</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="312"/> + <source>Not running</source> + <translation>Não está em execução</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="313"/> + <source>Disabled</source> + <translation>Desabilitado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="314"/> + <source>Running</source> + <translation>Em execução</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1188"/> + <source> Your are about to delete this rule. </source> + <translation> Você está prestes a excluir esta regra. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1710"/> + <source> Are you sure?</source> + <translation> Você tem certeza?</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="635"/> + <source>OpenSnitch Network Statistics {0}</source> + <translation>Estatísticas da rede OpenSnitch {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="637"/> + <source>OpenSnitch Network Statistics for {0}</source> + <translation>Estatísticas da rede OpenSnitch para {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> + <source>Name</source> + <translation type="obsolete">Nome</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Address</source> + <translation type="obsolete">Endereço</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="176"/> + <source>Status</source> + <translation type="obsolete">Estado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="183"/> + <source>Version</source> + <translation type="obsolete">Versão</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="833"/> + <source>Rules</source> + <translation>Regras</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Time</source> + <translation type="obsolete">Tempo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="874"/> + <source>Action</source> + <translation>Ação</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> + <source>Duration</source> + <translation type="obsolete">Duração</translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="18"/> + <source>Hits</source> + <translation>Acertos</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>Protocol</source> + <translation type="obsolete">Protocolo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2597"/> + <source>Save as CSV</source> + <translation>Salvar como CSV</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Enabled</source> + <translation type="obsolete">Ativado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="960"/> + <source>Delete</source> + <translation>Deletar</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="580"/> + <source><b>Error:</b><br><br>{0}</source> + <translation type="obsolete">&lt;b&gt;Erro:&lt;/b&gt;&lt;br&gt;&lt;br&gt;{0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="953"/> + <source>Disable</source> + <translation>Desabilitar</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="955"/> + <source>Enable</source> + <translation>Habilitar</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="958"/> + <source>Duplicate</source> + <translation>Duplicado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="959"/> + <source>Edit</source> + <translation>Editar</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1247"/> + <source>Rule not found by that name and node</source> + <translation>Regra não encontrada por esse nome e node</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1300"/> + <source><b>Error:</b><br><br></source> + <comment>{0}</comment> + <translation><b>Erro:</b><br><br></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1307"/> + <source>Warning:</source> + <translation>Atenção:</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="939"/> + <source>Allow</source> + <translation>Permitir</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="940"/> + <source>Deny</source> + <translation>Negar</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="944"/> + <source>Always</source> + <translation>Sempre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="945"/> + <source>Until reboot</source> + <translation>Até reiniciar</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1710"/> + <source> You are about to delete this rule. </source> + <translation> Você está prestes a excluir esta regra. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> + <source>Rule</source> + <translation type="obsolete">Regra</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> + <source>Name</source> + <comment>xxxxx</comment> + <translation type="obsolete">Nome</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> + <source>Name</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Nome</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Address</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Endereço</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> + <source>Status</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Estado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> + <source>Version</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Versão</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> + <source>Rules</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Regras</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Time</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Tempo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> + <source>Action</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Ação</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> + <source>Duration</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Duração</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Enabled</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Ativado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> + <source>Hits</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Acertos</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>Protocol</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Protocolo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> + <source>Rule</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Regra</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="286"/> + <source>Name</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Nome</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="287"/> + <source>Address</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Endereço</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="288"/> + <source>Status</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Estado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="289"/> + <source>Hostname</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Hostname</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="422"/> + <source>Version</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Versão</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="419"/> + <source>Rules</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Regras</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> + <source>Time</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Tempo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Action</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Ação</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> + <source>Duration</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Duração</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> + <source>Node</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Node</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> + <source>Enabled</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Habilitado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="437"/> + <source>Hits</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Acessos</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> + <source>Protocol</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Protocolo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> + <source>Process</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Processo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> + <source>Destination</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Destino</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> + <source>Rule</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Regra</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> + <source>UserID</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>UserID</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> + <source>LastConnection</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>UltimaConexao</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> + <source>Args</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="obsolete">Args</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>DstIP</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>DstIP</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> + <source>DstHost</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>DstHost</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> + <source>DstPort</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>TempoAtividade</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="179"/> + <source>Uptime</source> + <translation type="obsolete">Tempo de atividade</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="181"/> + <source>Connections</source> + <translation type="obsolete">Conexões</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="182"/> + <source>Dropped</source> + <translation type="obsolete">Dropado</translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="17"/> + <source>What</source> + <translation>Qual</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="941"/> + <source>Reject</source> + <translation>Rejeitar</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="930"/> + <source>Apply to</source> + <translation>Aplicar para</translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="19"/> + <source>Network name</source> + <translation>Nome da rede</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="290"/> + <source>Uptime</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Tempo de atividade</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="420"/> + <source>Connections</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Conexoes</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="421"/> + <source>Dropped</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Dropado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="436"/> + <source>What</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Qual</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Precedence</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Precedencia</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="794"/> + <source>New node connected</source> + <translation>Novo node conectado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> + <source>Description</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Descrição</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Cmdline</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Terminal</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="405"/> + <source>Export rules</source> + <translation>Exportar regras</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="406"/> + <source>Import rules</source> + <translation>Importar regras</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="407"/> + <source>Export events to CSV</source> + <translation>Exportar eventos para CSV</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="408"/> + <source>Quit</source> + <translation>Sair</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="931"/> + <source>Export</source> + <translation>Exportar</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="963"/> + <source>To clipboard</source> + <translation>Para a área de transferência</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="964"/> + <source>To disk</source> + <translation>Para o disco</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2539"/> + <source>Select a directory to export rules</source> + <translation>Selecione um diretório para exportar regras</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1190"/> + <source> Your are about to delete this entry. </source> + <translation> Você está prestes a excluir esta entrada. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1677"/> + <source> You are about to delete this node. </source> + <translation> Você está prestes a excluir este node. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1686"/> + <source><b>Error deleting node</b><br><br></source> + <comment>{0}</comment> + <translation><b>Erro ao excluir node</b><br><br></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2494"/> + <source>Error exporting rules</source> + <translation>Erro ao exportar regras</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2568"/> + <source>Select a directory with rules to import (JSON files)</source> + <translation>Selecione um diretório com regras para importar (arquivos JSON)</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2582"/> + <source>Rules imported fine</source> + <translation>Regras importadas corretamente</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="229"/> + <source>WARNING</source> + <translation>AVISO</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="832"/> + <source>Details</source> + <translation>Detalhes</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="834"/> + <source>New</source> + <translation>Novo</translation> + </message> +</context> +</TS> diff --git a/ui/i18n/locales/ro_RO/opensnitch-ro_RO.ts b/ui/i18n/locales/ro_RO/opensnitch-ro_RO.ts new file mode 100644 index 0000000..436f5e0 --- /dev/null +++ b/ui/i18n/locales/ro_RO/opensnitch-ro_RO.ts @@ -0,0 +1,2957 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="ro_RO"> +<context> + <name>Dialog</name> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="34"/> + <source>opensnitch-qt</source> + <translation>opensnitch-qt</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="702"/> + <source>from this executable</source> + <translation>din acest executabil</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="707"/> + <source>from this command line</source> + <translation>din această linie de comandă</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="712"/> + <source>this destination port</source> + <translation>acest port de destinație</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="717"/> + <source>this user</source> + <translation>acest utilizator</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="722"/> + <source>this destination ip</source> + <translation>această adresă IP de destinație</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="865"/> + <source>+</source> + <translation>+</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="751"/> + <source>once</source> + <translation>o dată</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="756"/> + <source>30s</source> + <translation>30s</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="761"/> + <source>5m</source> + <translation>5m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="766"/> + <source>15m</source> + <translation>15m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="771"/> + <source>30m</source> + <translation>30m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="776"/> + <source>1h</source> + <translation>1o</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="781"/> + <source>until reboot</source> + <translation>până la repornire</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="786"/> + <source>forever</source> + <translation>mereu</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="346"/> + <source>Deny</source> + <translation>Refuză</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="337"/> + <source>Allow</source> + <translation>Permite</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="300"/> + <source>User ID</source> + <translation>ID utilizator</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="334"/> + <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source> + <translation><html><head/><body><p><span style=" font-weight:600;">Executat din</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="647"/> + <source>TextLabel</source> + <translation>EtichetăText</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="437"/> + <source>Source IP</source> + <translation>Adresă IP sursă</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="458"/> + <source>Process ID</source> + <translation>ID proces</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="601"/> + <source>Destination IP</source> + <translation>Adresă IP destinație</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="622"/> + <source>Dst Port</source> + <translation>Port destinație</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="727"/> + <source>from this PID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="809"/> + <source>action</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="14"/> + <source>Firewall</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="55"/> + <source><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="320"/> + <source>Inbound</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="313"/> + <source>Outbound</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="275"/> + <source>Profile</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="375"/> + <source>Allow inbound connections to a port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="378"/> + <source>Allow service (IN)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="397"/> + <source>Exclude outbound connections to a port from being intercepted</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="406"/> + <source>Allow service (OUT)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="426"/> + <source>New rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="431"/> + <source>Close</source> + <translation type="unfinished">Închide</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="14"/> + <source>Firewall rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="26"/> + <source>Node</source> + <translation type="unfinished">Nod</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="38"/> + <source>Enable</source> + <translation type="unfinished">Activează</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="50"/> + <source>Description</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="90"/> + <source>Simple</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="154"/> + <source>Add new condition</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="177"/> + <source>Remove selected condition</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="233"/> + <source>Direction</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="248"/> + <source>IN</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="257"/> + <source>OUT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="223"/> + <source>Action</source> + <translation type="unfinished">Acțiune</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="285"/> + <source>ACCEPT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="294"/> + <source>DROP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="303"/> + <source>REJECT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="312"/> + <source>RETURN</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="442"/> + <source>Clear</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="453"/> + <source>Delete</source> + <translation type="unfinished">Șterge</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="464"/> + <source>Save</source> + <translation type="unfinished">Salvează</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="475"/> + <source>Add</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="266"/> + <source>FORWARD</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="271"/> + <source>PREROUTING</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="276"/> + <source>POSTROUTING</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="321"/> + <source>QUEUE</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="330"/> + <source>DNAT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="335"/> + <source>SNAT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="340"/> + <source>REDIRECT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="359"/> + <source>depending on the Action (i.e.: target), the syntaxis of the parameters will vary. +Some examples: + +QUEUE -> num 0 (or 1, 2, ...) +REDIRECT, TPROXY, DNAT, SNAT, MASQUERADE: + to :22 + to 192.168.1.254:8080 + to 192.168.1.254 + to 1024-2048 (masquerade)</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>PreferencesDialog</name> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="14"/> + <source>Preferences</source> + <translation>Preferințe</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="484"/> + <source>UI</source> + <translation>Interfață utilizator</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="359"/> + <source>Show advanced view by default</source> + <translation>Arată implicit vizualizarea avansată</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="970"/> + <source>once</source> + <translation>o dată</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="219"/> + <source>30s</source> + <translation>30s</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="224"/> + <source>5m</source> + <translation>5m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="229"/> + <source>15m</source> + <translation>15m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="234"/> + <source>30m</source> + <translation>30m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="239"/> + <source>1h</source> + <translation>1o</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="244"/> + <source>until reboot</source> + <translation>până la repornire</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="249"/> + <source>forever</source> + <translation>mereu</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="653"/> + <source>Action</source> + <translation>Acțiune</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="323"/> + <source>Default target</source> + <translation>Țintă implicită</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="375"/> + <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source> + <translation><html><head/><body><p>Dacă este bifată, ferestrele de notificare care apar vor fi afișate cu vizualizarea avansată activă.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1012"/> + <source>deny</source> + <translation>refuză</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1021"/> + <source>allow</source> + <translation>permite</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="283"/> + <source>by executable</source> + <translation>după executabil</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="288"/> + <source>by command line</source> + <translation>după linia de comandă</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="293"/> + <source>by destination port</source> + <translation>după portul de destinație</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="298"/> + <source>by destination ip</source> + <translation>după adresa IP de destinație</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="303"/> + <source>by user id</source> + <translation>după identificatorul utilizatorului</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="179"/> + <source>center</source> + <translation>centru</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="184"/> + <source>top right</source> + <translation>sus la dreapta</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="189"/> + <source>bottom right</source> + <translation>jos la dreapta</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="194"/> + <source>top left</source> + <translation>sus la stânga</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="199"/> + <source>bottom left</source> + <translation>jos la stânga</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="340"/> + <source>Pop-up default duration</source> + <translation>Durată implicită fereastră de notificare</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="343"/> + <source>Duration</source> + <translation>Durată</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="263"/> + <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source> + <translation><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="266"/> + <source>Filter connections also by:</source> + <translation>Filtrează conexiunile și după:</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="81"/> + <source>User ID</source> + <translation>ID utilizator</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="97"/> + <source>Destination port</source> + <translation>Port destinație</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="113"/> + <source>Destination IP</source> + <translation>Adresă IP destinație</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="406"/> + <source>Disable pop-ups, only display an alert</source> + <translation type="obsolete">Dezactivează ferestrele de notificare, arată doar o alertă</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="463"/> + <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source> + <translation><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="466"/> + <source>Default timeout</source> + <translation>Durată implicită pentru alegere</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="823"/> + <source>Nodes</source> + <translation>Noduri</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="829"/> + <source>Process monitor method</source> + <translation>Metodă monitorizare procese</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="846"/> + <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source> + <translation><html><head/><body><p>Fișierul jurnal unde să se scrie jurnalizări.<br/></p><p>/dev/stdout va tipări jurnalizările pe ieșirea standard.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="849"/> + <source>Log file</source> + <translation>Fișier de jurnalizare</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="863"/> + <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source> + <translation><html><head/><body><p>Durata implicită va fi folosită când nu este conectată nicio interfață grafică.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="866"/> + <source>Default duration</source> + <translation>Durată implicită</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="879"/> + <source>Apply configuration to all nodes</source> + <translation>Aplică configurația la toate nodurile</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="902"/> + <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source> + <translation><html><head/><body><p>Acțiunea implicită va fi folosită când nu este conectată nicio interfață grafică.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="921"/> + <source>HostName</source> + <translation>NumeGazdă</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="975"/> + <source>until restart</source> + <translation>până la repornire</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="980"/> + <source>always</source> + <translation>întotdeauna</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="988"/> + <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source> + <translation><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="991"/> + <source>Address</source> + <translation>Adresă</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1039"/> + <source>Version</source> + <translation>Versiune</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1090"/> + <source>unix:///tmp/osui.sock</source> + <translation>unix:///tmp/osui.sock</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1102"/> + <source>/var/log/opensnitchd.log</source> + <translation>/var/log/opensnitchd.log</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1107"/> + <source>/dev/stdout</source> + <translation>/dev/stdout</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1131"/> + <source>Default log level</source> + <translation>Nivel implicit de jurnalizare</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1146"/> + <source>Database</source> + <translation>Bază de date</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1200"/> + <source>Database type</source> + <translation>Tip bază de date</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1207"/> + <source>Select</source> + <translation>Selectare</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1181"/> + <source>In memory</source> + <translation>În memorie</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1186"/> + <source>File</source> + <translation>Fișier</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1482"/> + <source>Close</source> + <translation>Închide</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1493"/> + <source>Apply</source> + <translation>Aplică</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1504"/> + <source>Save</source> + <translation>Salvează</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="356"/> + <source>The advanced view allows you to easily select multiple fields to filter connections</source> + <translation>Vizualizarea avansată vă permite să selectați cu ușurință câmpuri multiple pentru a filtra conexiunile</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="110"/> + <source>If checked, this field will be selected when a pop-up is displayed</source> + <translation>Dacă este bifată, acest câmp va fi selectat când o fereastră de notificare este afișată</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="159"/> + <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source> + <translation><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="905"/> + <source>Default action when the GUI is disconnected</source> + <translation>Acțiune implicită când interfața grafică cu utilizatorul este deconectată</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1001"/> + <source>Debug invalid connections</source> + <translation>Depanează conexiunile nevalide</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="39"/> + <source>Pop-ups</source> + <translation>Ferestre de notificare</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="64"/> + <source>Default options</source> + <translation>Opțiuni implicite</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="330"/> + <source>Default position on screen</source> + <translation>Compozziția implicită pe ecran</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="769"/> + <source>any temporary rules</source> + <translation>oricare regulă temporară</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="450"/> + <source><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="453"/> + <source>Don't save rules of duration</source> + <translation type="obsolete">Nu salva regulile de durată</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="589"/> + <source>Time</source> + <translation>Timp</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="669"/> + <source>Destination</source> + <translation>Destinație</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="637"/> + <source>Protocol</source> + <translation>Protocol</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="685"/> + <source>Process</source> + <translation>Proces</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="605"/> + <source>Rule</source> + <translation>Regulă</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="621"/> + <source>Node</source> + <translation>Nod</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="723"/> + <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using wireguard.</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using wireguard.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="577"/> + <source>Events tab columns</source> + <translation>Coloane etichete evenimente</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="147"/> + <source>reject</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="308"/> + <source>by PID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="473"/> + <source>Disable pop-ups, only display a notification</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="496"/> + <source>Desktop notifications</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="514"/> + <source>Use system notifications</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="530"/> + <source>Use Qt notifications</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="559"/> + <source>Test</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="716"/> + <source>System</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="695"/> + <source>Command line</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="708"/> + <source>Theme</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="748"/> + <source>Rules</source> + <translation type="unfinished">Reguli</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="756"/> + <source>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI. + +Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="761"/> + <source>Don't save/Delete rules of duration</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="779"/> + <source>30s or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="784"/> + <source>5m or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="789"/> + <source>15m or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="794"/> + <source>30m or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="799"/> + <source>1h or less</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="998"/> + <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1294"/> + <source>minutes</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1326"/> + <source>Minutes between events purges</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1352"/> + <source>days</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1365"/> + <source>Maximum days of events to keep</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="724"/> + <source>Language</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>ProcessDetailsDialog</name> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="14"/> + <source>Process details</source> + <translation>Detalii proces</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="61"/> + <source>loading...</source> + <translation>Se încarcă...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="81"/> + <source>CWD: loading...</source> + <translation>CWD: loading...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="93"/> + <source>mem stats: loading...</source> + <translation>Statistici memorie: se încarcă...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="121"/> + <source>Status</source> + <translation>Stare</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="135"/> + <source>Open files</source> + <translation>Deschidere fișiere</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="149"/> + <source>I/O Statistics</source> + <translation>Statistici intrare/ieșire</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="163"/> + <source>Memory mapped files</source> + <translation>Fișiere cartografiate în memorie</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="177"/> + <source>Stack</source> + <translation>Stivă</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="191"/> + <source>Environment variables</source> + <translation>Variabile de mediu</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="210"/> + <source>Application pids</source> + <translation>Identificatori procese aplicație</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="240"/> + <source>Start or stop monitoring this process</source> + <translation>Pornește sau oprește monitorizarea acestui proces</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="256"/> + <source>Close</source> + <translation>Închide</translation> + </message> +</context> +<context> + <name>RulesDialog</name> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="20"/> + <source>Rule</source> + <translation>Regulă</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> + <source>Node</source> + <translation>Nod</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> + <source>Apply rule to all nodes</source> + <translation>Aplică regula la toate nodurile</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="610"/> + <source>To this IP / Network</source> + <translation>Pentru această adresă IP / rețea</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="138"/> + <source>/path/to/executable, .*/bin/executable[0-9\.]+$, ...</source> + <translation type="obsolete">/cale/către/executabil, .*/bin/executabil[0-9\.]+$, ...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="56"/> + <source>Action</source> + <translation>Acțiune</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="902"/> + <source>To this port</source> + <translation>Pentru acest port</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="980"/> + <source>To this list of domains</source> + <translation>Pentru această listă de domenii</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="760"/> + <source>You can specify a single IP: +- 192.168.1.1 + +or a regular expression: +- 192\.168\.1\.[0-9]+ + +multiple IPs: +- ^(192\.168\.1\.1|172\.16\.0\.1)$ + +You can also specify a subnet: +- 192.168.1.0/24 + +Note: Commas or spaces are not allowed to separate IPs or networks.</source> + <translation>You can specify a single IP: +- 192.168.1.1 + +or a regular expression: +- 192\.168\.1\.[0-9]+ + +multiple IPs: +- ^(192\.168\.1\.1|172\.16\.0\.1)$ + +You can also specify a subnet: +- 192.168.1.0/24 + +Note: Commas or spaces are not allowed to separate IPs or networks.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="214"/> + <source>LAN</source> + <translation type="obsolete">Rețea locală (LAN)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="219"/> + <source>127.0.0.0/8</source> + <translation type="obsolete">127.0.0.0/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="224"/> + <source>192.168.0.0/24</source> + <translation type="obsolete">192.168.0.0/24</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="229"/> + <source>192.168.1.0/24</source> + <translation type="obsolete">192.168.1.0/24</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="234"/> + <source>192.168.2.0/24</source> + <translation type="obsolete">192.168.2.0/24</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="239"/> + <source>192.168.0.0/16</source> + <translation type="obsolete">192.168.0.0/16</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="244"/> + <source>169.254.0.0/16</source> + <translation type="obsolete">169.254.0.0/16</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="249"/> + <source>172.16.0.0/12</source> + <translation type="obsolete">172.16.0.0/12</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="254"/> + <source>10.0.0.0/8</source> + <translation type="obsolete">10.0.0.0/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="259"/> + <source>::1/128</source> + <translation type="obsolete">::1/128</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="264"/> + <source>fc00::/7</source> + <translation type="obsolete">fc00::/7</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="269"/> + <source>ff00::/8</source> + <translation type="obsolete">ff00::/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="274"/> + <source>fe80::/10</source> + <translation type="obsolete">fe80::/10</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="279"/> + <source>fd00::/8</source> + <translation type="obsolete">fd00::/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="686"/> + <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="97"/> + <source>once</source> + <translation>o dată</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> + <source>30s</source> + <translation type="obsolete">30s</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="328"/> + <source>5m</source> + <translation type="obsolete">5m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="333"/> + <source>15m</source> + <translation type="obsolete">15m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="338"/> + <source>30m</source> + <translation type="obsolete">30m</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="343"/> + <source>1h</source> + <translation type="obsolete">1o</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="127"/> + <source>until reboot</source> + <translation>până la repornire</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="132"/> + <source>always</source> + <translation>întotdeauna</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/> + <source>Commas or spaces are not allowed to specify multiple domains. + +Use regular expressions instead: +.*(opensnitch|duckduckgo).com +.*\.google.com + +or a single domain: +www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... +gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source> + <translation>Commas or spaces are not allowed to specify multiple domains. + +Use regular expressions instead: +.*(opensnitch|duckduckgo).com +.*\.google.com + +or a single domain: +www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... +gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="603"/> + <source>www.domain.org, .*\.domain.org</source> + <translation>www.domeniu.org, .*\.domeniu.org</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="750"/> + <source>To this host</source> + <translation>Pentru această gazdă</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="89"/> + <source>Duration</source> + <translation>Durată</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="526"/> + <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source> + <translation><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="532"/> + <source>TCP</source> + <translation>TCP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="421"/> + <source>UDP</source> + <translation type="obsolete">UDP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="426"/> + <source>UDPLITE</source> + <translation type="obsolete">UDPLITE</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="431"/> + <source>TCP6</source> + <translation type="obsolete">TCP6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="436"/> + <source>UDP6</source> + <translation type="obsolete">UDP6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="441"/> + <source>UDPLITE6</source> + <translation type="obsolete">UDPLITE6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="633"/> + <source>Protocol</source> + <translation>Protocol</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="472"/> + <source>From this executable</source> + <translation>De la acest executabil</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="151"/> + <source>Deny</source> + <translation>Refuză</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="191"/> + <source>Allow</source> + <translation>Permite</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="379"/> + <source>From this command line</source> + <translation>De la această linie de comandă</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="372"/> + <source>From this user ID</source> + <translation>De la acest ID utilizator</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="539"/> + <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="245"/> + <source>Name</source> + <translation type="unfinished">Nume</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="207"/> + <source>Enable</source> + <translation>Activează</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="238"/> + <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them. + +000-allow-localhost +001-deny-broadcast +...</source> + <translation>Regulile sunt verificate în ordine alfabetică, așa că puteți să le numiți ca atare pentru a le prioritiza. + +000-permite-localhost +001-respinge-broadcast +...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="611"/> + <source>leave blank to autocreate</source> + <translation type="obsolete">lăsați gol pentru creare automată</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="214"/> + <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one. + +You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example: + +[x] Priority - 000-priority-rule +[ ] Priority - 001-less-priority-rule</source> + <translation>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one. + +You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example: + +[x] Priority - 000-priority-rule +[ ] Priority - 001-less-priority-rule</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="222"/> + <source>Priority rule</source> + <translation>Regulă prioritate</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1092"/> + <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source> + <translation><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1095"/> + <source>Case-sensitive</source> + <translation>Sensibil la majuscule</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="148"/> + <source>Deny will just discard the connection</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="165"/> + <source>Reject will drop the connection, and kill the socket that initiated it</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="168"/> + <source>Reject</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="185"/> + <source>Allow will allow the connection</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="346"/> + <source>Applications</source> + <translation type="unfinished">Aplicații</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="355"/> + <source><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="365"/> + <source>Is regular expression</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="389"/> + <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="399"/> + <source>From this PID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> + <source>is regular expression</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="491"/> + <source>Network</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="566"/> + <source>ICMP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="571"/> + <source>ICMP6</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="576"/> + <source>SCTP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="581"/> + <source>SCTP6</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="863"/> + <source>Network interface</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="932"/> + <source>List of domains/IPs</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="938"/> + <source>To this list of network ranges</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="945"/> + <source>To this list of IPs</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="971"/> + <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1006"/> + <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1034"/> + <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1049"/> + <source>To this list of domains +(regular expressions)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1076"/> + <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1086"/> + <source>More</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1102"/> + <source>Don't log connections that match this rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1105"/> + <source>Don't log connections</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1151"/> + <source>Description...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="743"/> + <source>From this IP / Network</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="872"/> + <source>From this port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="918"/> + <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>StatsDialog</name> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="34"/> + <source>OpenSnitch Network Statistics</source> + <translation>Statistici de rețea OpenSnitch</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="105"/> + <source>Save to CSV</source> + <translation type="obsolete">Salvează într-un fișier CSV.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="115"/> + <source>Ctrl+S</source> + <translation type="obsolete">Ctrl+S</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="333"/> + <source>Create a new rule</source> + <translation>Creare regulă nouă</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="376"/> + <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source> + <translation><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="413"/> + <source>Status</source> + <translation>Stare</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1793"/> + <source>-</source> + <translation>-</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="451"/> + <source>Start or Stop interception</source> + <translation>Porniți sau opriți interceptarea</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="496"/> + <source>Events</source> + <translation>Evenimente</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="94"/> + <source>Filter</source> + <translation>Filtru</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="107"/> + <source>Allow</source> + <translation>Permite</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="116"/> + <source>Deny</source> + <translation>Refuză</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="143"/> + <source>Ex.: firefox</source> + <translation>De exemplu: firefox</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="205"/> + <source>50</source> + <translation>50</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="210"/> + <source>100</source> + <translation>100</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="215"/> + <source>200</source> + <translation>200</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="220"/> + <source>300</source> + <translation>300</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="233"/> + <source>Delete all intercepted events</source> + <translation>Șterge toate evenimentele de interceptare</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="826"/> + <source>Nodes</source> + <translation>Noduri</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="554"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Addr column to view details of a node)</span></p></body></html></source> + <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(double click on the Addr column to view details of a node)</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1700"/> + <source>Rules</source> + <translation>Reguli</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="995"/> + <source>enable</source> + <translation>Activează</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1022"/> + <source>Edit rule</source> + <translation>Editare regulă</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1039"/> + <source>Delete rule</source> + <translation>Șterge regula</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="699"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click on a row to view details of a rule)</span></p></body></html></source> + <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(faceți clic dublu pe un rând pentru a vizualiza detaliile regulii)</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="692"/> + <source>search rule name</source> + <translation type="obsolete">Căutare nume regulă</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="782"/> + <source>Application rules</source> + <translation>Reguli aplicație</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="936"/> + <source>Permanent</source> + <translation type="unfinished">Permanent</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="945"/> + <source>Temporary</source> + <translation>Temporar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1063"/> + <source>Hosts</source> + <translation>Gazde</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1364"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click to view details of an item)</span></p></body></html></source> + <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(faceți clic dublu pentru a vizualiza detaliile unui element)</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1153"/> + <source>Applications</source> + <translation>Aplicații</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1051"/> + <source>Delete all intercepted applications</source> + <translation type="obsolete">Șterge toate aplicațiile interceptate</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1266"/> + <source>Addresses</source> + <translation>Adrese</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1356"/> + <source>Ports</source> + <translation>Porturi</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1440"/> + <source>Users</source> + <translation>Utilizatori</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1544"/> + <source>Connections</source> + <translation>Conexiuni</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1596"/> + <source>Dropped</source> + <translation>Aruncate</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1648"/> + <source>Uptime</source> + <translation>Durată activitate</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1767"/> + <source>Version</source> + <translation>Versiune</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="665"/> + <source>Delete connections that matched this rule</source> + <translation type="obsolete">Șterge toate conexiunile care se potrivesc cu această regulă</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="927"/> + <source>All applications</source> + <translation>Toate aplicațiile</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="926"/> + <source>Delete all intercepted hosts</source> + <translation type="obsolete">Șterge toate gazdele interceptate</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1159"/> + <source>Delete all intercepted addresses</source> + <translation type="obsolete">Șterge toate adresele interceptate</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1261"/> + <source>Delete all intercepted ports</source> + <translation type="obsolete">Șterge toate porturile interceptate</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1371"/> + <source>Delete all intercepted users</source> + <translation type="obsolete">Șterge toți utilizatorii interceptați</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="125"/> + <source>Reject</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="180"/> + <source>0</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="637"/> + <source>Delete this node</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="653"/> + <source>Show the preferences of this node</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="669"/> + <source>Start or stop interception of this node</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="777"/> + <source>2</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="954"/> + <source>System rules</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>contextual_menu</name> + <message> + <location filename="../../../opensnitch/service.py" line="47"/> + <source>Statistics</source> + <translation>Statistici</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="48"/> + <source>Enable</source> + <translation>Activează</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="49"/> + <source>Disable</source> + <translation>Dezactivează</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="50"/> + <source>Help</source> + <translation>Ajutor</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="51"/> + <source>Close</source> + <translation>Închide</translation> + </message> +</context> +<context> + <name>firewall</name> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="91"/> + <source>Configuration applied.</source> + <translation type="unfinished">Configurația a fost aplicată.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="404"/> + <source>Error: {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="193"/> + <source>Applying changes...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="230"/> + <source>Error getting INPUT chain policy</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="237"/> + <source>Error getting OUTPUT chain policy</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="290"/> + <source>In order to configure firewall rules from the GUI, we need to use 'nftables' instead of 'iptables'</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="304"/> + <source>Enabling firewall...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="306"/> + <source>Disabling firewall...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="71"/> + <source>Dest Port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="72"/> + <source>Source Port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="73"/> + <source>Dest IP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="74"/> + <source>Source IP</source> + <translation type="unfinished">Adresă IP sursă</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="75"/> + <source>Input interface</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="76"/> + <source>Output interface</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="77"/> + <source>Set conntrack mark</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="78"/> + <source>Match conntrack mark</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="79"/> + <source>Match conntrack state(s)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="80"/> + <source>Set mark on packet</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="81"/> + <source>Match packet information</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="87"/> + <source>Bandwidth quotas</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="89"/> + <source>Rate limit connections</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="373"/> + <source>Your protobuf version is incompatible, you need to install protobuf 3.8.0 or superior +(pip3 install --ignore-installed protobuf==3.8.0)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="397"/> + <source>Rule deleted</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="401"/> + <source>Rule added</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="420"/> + <source>You can use ',' or '-' to specify multiple ports/IPs or ranges/values:<br><br>ports: 22 or 22,443 or 50000-60000<br>IPs: 192.168.1.1 or 192.168.1.30-192.168.1.130<br>Values: echo-reply,echo-request<br>Values: new,established,related</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="440"/> + <source>Deleting rule, wait</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="443"/> + <source>Error updating rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="483"/> + <source>Adding rule, wait</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="492"/> + <source><select a statement></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="787"/> + <source>Equal</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="788"/> + <source>Not equal</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="789"/> + <source>Greater or equal than</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="790"/> + <source>Greater than</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="791"/> + <source>Less or equal than</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="792"/> + <source>Less than</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1350"/> + <source>Firewall rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="885"/> + <source>Simple</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="890"/> + <source>Advanced</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1046"/> + <source>This rule is not supported yet.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1111"/> + <source>Exclude service</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1123"/> + <source>Allow inbound connections to the selected port.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1125"/> + <source>Allow outbound connections to the selected port.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1201"/> + <source>select a statement.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1217"/> + <source>value cannot be 0 or empty.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1229"/> + <source>the value format is 1024/kbytes (or bytes, mbytes, gbytes)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1240"/> + <source>the value format is 1024/kbytes/second (or bytes, mbytes, gbytes)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1243"/> + <source>rate-limit not valid, use: bytes, kbytes, mbytes or gbytes.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1245"/> + <source>time-limit not valid, use: second, minute, hour or day</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1293"/> + <source>port not valid.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="108"/> + <source> +Supported formats: + + - Simple: 23 + - Ranges: 80-1024 + - Multiple ports: 80,443,8080 +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="134"/> + <source> +Supported formats: + + - Simple: 1.2.3.4 + - IP ranges: 1.2.3.100-1.2.3.200 + - Network ranges: 1.2.3.4/24 +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="147"/> + <source>Match input interface. Regular expressions not allowed.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="154"/> + <source>Match output interface. Regular expressions not allowed.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="161"/> + <source>Set a conntrack mark on the connection, in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="171"/> + <source>Match a conntrack mark of the connection, in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="178"/> + <source>Match conntrack states. + +Supported formats: + - Simple: new + - Multiple states separated by commas: related,new +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="193"/> + <source> +Match packet's metainformation. + +Value must be in decimal format, except for the "l4proto" option. +For l4proto it can be a lower case string, for example: + tcp + udp + icmp, + etc + +If the value is decimal for protocol or lproto, it'll use it as the code of +that protocol. +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="213"/> + <source>Set a mark on the packet matching the specified conditions. The value is in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="221"/> + <source> +Match ICMP codes. + +Supported formats: + - Simple: echo-request + - Multiple separated by commas: echo-request,echo-reply +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="234"/> + <source> +Match ICMPv6 codes. + +Supported formats: + - Simple: echo-request + - Multiple separated by commas: echo-request,echo-reply +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="247"/> + <source>Print a message when this rule matches a packet.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="254"/> + <source> +Apply quotas on connections. + +For example when: + - "quota over 10/mbytes" -> apply the Action defined (DROP) + - "quota until 10/mbytes" -> apply the Action defined (ACCEPT) + +The value must be in the format: VALUE/UNITS, for example: + - 10mbytes, 1/gbytes, etc +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="286"/> + <source> +Apply limits on connections. + +For example when: + - "limit over 10/mbytes/minute" -> apply the Action defined (DROP, ACCEPT, etc) + (When there're more than 10MB per minute, apply an Action) + + - "limit until 10/mbytes/hour" -> apply the Action defined (ACCEPT) + +The value must be in the format: VALUE/UNITS/TIME, for example: + - 10/mbytes/minute, 1/gbytes/hour, etc +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="607"/> + <source>num</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="621"/> + <source>to</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>messages</name> + <message> + <location filename="../../../opensnitch/service.py" line="281"/> + <source>Info</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="285"/> + <source>Error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="289"/> + <source>Warning</source> + <translation type="unfinished">Avertisment</translation> + </message> +</context> +<context> + <name>notifications</name> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="654"/> + <source>System notifications are not available, you need to install python3-notify2.</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>popups</name> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="50"/> + <source>until reboot</source> + <translation>până la repornire</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/> + <source>forever</source> + <translation>mereu</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="115"/> + <source>Allow</source> + <translation>Permite</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="114"/> + <source>Deny</source> + <translation>Refuză</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="331"/> + <source>Outgoing connection</source> + <translation>Conexiune de ieșire</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="336"/> + <source>Process launched from:</source> + <translation>Procesul a fost lansat din:</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="373"/> + <source>from this executable</source> + <translation>de la acest executabil</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="377"/> + <source>from this command line</source> + <translation>de la această linie de comandă</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="379"/> + <source>to port {0}</source> + <translation>către portul {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="442"/> + <source>to {0}</source> + <translation>către {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="382"/> + <source>from user {0}</source> + <translation>de la utilizatorul {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="399"/> + <source>to {0}.*</source> + <translation>către {0}.*</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="452"/> + <source>to *.{0}</source> + <translation>către *.{0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="378"/> + <source>to *{0}</source> + <translation type="obsolete">către *{0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="486"/> + <source><b>Remote</b> process %s running on <b>%s</b></source> + <translation>Procesul %s<b>telecomandat</b> rulează pe <b>%s</b></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="490"/> + <source>is connecting to <b>%s</b> on %s port %d</source> + <translation>se conectează la <b>%s</b> pe %s portul %d</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="502"/> + <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source> + <translation>încearcă să rezolve <b>%s</b> prin %s, %s portul %d</translation> + </message> + <message> + <location filename="../../../opensnitch/notifications.py" line="122"/> + <source>New outgoing connection</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="116"/> + <source>Reject</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="386"/> + <source>from this PID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="497"/> + <source>is connecting to <b>%s</b>, %s</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/notifications.py" line="42"/> + <source>Open</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>preferences</name> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="386"/> + <source>Exception saving config: {0}</source> + <translation>Exception saving config: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> + <source>Warning</source> + <translation>Avertisment</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> + <source>You must select a file for the database<br>or choose "In memory" type.</source> + <translation>You must select a file for the database<br>or choose "In memory" type.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="401"/> + <source>DB type changed</source> + <translation>Tipul bazei de date a fost schimbat</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="38"/> + <source>Restart the GUI in order effects to take effect</source> + <translation>Reporniți interfața grafică cu utilizatorul pentru ca modificările să aibă efect</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="500"/> + <source>Applying configuration on {0} ...</source> + <translation>Se aplică configurația pe {0} ...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="292"/> + <source>Server address can not be empty</source> + <translation>Adresa servitorului nu poate fi goală</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="321"/> + <source>Error loading {0} configuration</source> + <translation>Error loading {0} configuration</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="568"/> + <source>Configuration applied.</source> + <translation>Configurația a fost aplicată.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="570"/> + <source>Error applying configuration: {0}</source> + <translation>Eroare la aplicarea configurației: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="607"/> + <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source> + <translation>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="388"/> + <source>There're no nodes connected</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="466"/> + <source>System</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="187"/> + <source>Themes not available. Install qt-material: pip3 install qt-material</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> + <source>UI theme changed</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> + <source>Restart the GUI in order to apply the new theme</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="508"/> + <source>Ok</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="520"/> + <source>Exception saving node config {0}: {1}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="164"/> + <source>System default</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="433"/> + <source>Language changed</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>proc_details</name> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="100"/> + <source><b>Error loading process information:</b> <br><br> + +</source> + <translation><b>Eroare la încărcarea informațiilor procesului:</b> <br><br> + +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="119"/> + <source><b>Error stopping monitoring process:</b><br><br></source> + <translation><b>Eroare la oprirea monitorizării procesului:</b><br><br></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="159"/> + <source>loading...</source> + <translation>Se încarcă...</translation> + </message> +</context> +<context> + <name>rules</name> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="228"/> + <source>There're no nodes connected.</source> + <translation>Nu există niciun nod conectat.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="271"/> + <source>Rule applied.</source> + <translation>Regulă aplicată.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="273"/> + <source>Error applying rule: {0}</source> + <translation>Eroare la aplicarea regulii: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="539"/> + <source><b>Error loading rule</b></source> + <translation><b>Eroare la încărcarea regulii</b></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="641"/> + <source>protocol can not be empty, or uncheck it</source> + <translation>Protocolul nu poate fi gol, sau debifați-l</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="655"/> + <source>Protocol regexp error</source> + <translation>Protocol regexp error</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="659"/> + <source>process path can not be empty</source> + <translation>Calea procesului nu poate fi goală</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="673"/> + <source>Process path regexp error</source> + <translation>Process path regexp error</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="677"/> + <source>command line can not be empty</source> + <translation>Linia de comandă nu poate fi goală</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="691"/> + <source>Command line regexp error</source> + <translation>Command line regexp error</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="731"/> + <source>Dest port can not be empty</source> + <translation>Dest port can not be empty</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="745"/> + <source>Dst port regexp error</source> + <translation>Dst port regexp error</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="749"/> + <source>Dest host can not be empty</source> + <translation>Dest host can not be empty</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="763"/> + <source>Dst host regexp error</source> + <translation>Dst host regexp error</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="805"/> + <source>Dest IP/Network can not be empty</source> + <translation>Dest IP/Network can not be empty</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="831"/> + <source>Dst IP regexp error</source> + <translation>Dst IP regexp error</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="843"/> + <source>User ID can not be empty</source> + <translation>User ID can not be empty</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="857"/> + <source>User ID regexp error</source> + <translation>User ID regexp error</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="931"/> + <source>Lists field cannot be empty</source> + <translation>Lists field cannot be empty</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="933"/> + <source>Lists field must be a directory</source> + <translation>Lists field must be a directory</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="976"/> + <source><b>Rule not supported</b></source> + <translation><b>Regula nu este sprijinită</b></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="245"/> + <source>There's already a rule with this name.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="695"/> + <source>Network interface can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="709"/> + <source>Network interface regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="861"/> + <source>PID field can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="875"/> + <source>PID field regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="963"/> + <source>Select at least one field.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="713"/> + <source>Source port can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="727"/> + <source>Source port regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="767"/> + <source>Source IP/Network can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="793"/> + <source>Source IP regexp error</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>stats</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="313"/> + <source>Not running</source> + <translation>Nu rulează</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="314"/> + <source>Disabled</source> + <translation>Dezactivată</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="315"/> + <source>Running</source> + <translation>Rulează</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="636"/> + <source>OpenSnitch Network Statistics {0}</source> + <translation>OpenSnitch Network Statistics {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="638"/> + <source>OpenSnitch Network Statistics for {0}</source> + <translation>OpenSnitch Network Statistics for {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1301"/> + <source><b>Error:</b><br><br></source> + <comment>{0}</comment> + <translation><b>Eroare:</b><br><br></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1308"/> + <source>Warning:</source> + <translation>Avertisment:</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="940"/> + <source>Allow</source> + <translation>Permite</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="941"/> + <source>Deny</source> + <translation>Refuză</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="945"/> + <source>Always</source> + <translation>Întotdeauna</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="946"/> + <source>Until reboot</source> + <translation>Până la repornire</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="954"/> + <source>Disable</source> + <translation>Dezactivează</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="956"/> + <source>Enable</source> + <translation>Activează</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="959"/> + <source>Duplicate</source> + <translation>Duplică</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="960"/> + <source>Edit</source> + <translation>Editare</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="961"/> + <source>Delete</source> + <translation>Șterge</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1189"/> + <source> Your are about to delete this rule. </source> + <translation> Sunteți pe cale să ștergeți aceasă regulă. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> + <source> Are you sure?</source> + <translation> Sigur doriți acest lucru?</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1248"/> + <source>Rule not found by that name and node</source> + <translation>Regula nu a putut fi găsită după acel nume și nod</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> + <source> You are about to delete this rule. </source> + <translation> Sunteți pe cale să ștergeți această regulă. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2581"/> + <source>Save as CSV</source> + <translation>Save as CSV</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="287"/> + <source>Name</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Nume</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="288"/> + <source>Address</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Adresă</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="289"/> + <source>Status</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Stare</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="290"/> + <source>Hostname</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Nume gazdă</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="423"/> + <source>Version</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Versiune</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="420"/> + <source>Rules</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Reguli</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Time</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Timp</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> + <source>Action</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Acțiune</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> + <source>Duration</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Durată</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> + <source>Node</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Nod</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Enabled</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Activată</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="438"/> + <source>Hits</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Atingeri</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> + <source>Protocol</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Protocol</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Process</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Proces</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>Destination</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Destinație</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> + <source>Rule</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Regulă</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> + <source>UserID</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>IdentificatorUtilizator</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> + <source>LastConnection</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>UltimaConexiune</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="275"/> + <source>Args</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="obsolete">Argumente</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> + <source>DstIP</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>DstIP</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> + <source>DstHost</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>DstHost</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> + <source>DstPort</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>DstPort</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="776"/> + <source>New node connected</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="17"/> + <source>What</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="18"/> + <source>Hits</source> + <translation type="unfinished">Atingeri</translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="19"/> + <source>Network name</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="291"/> + <source>Uptime</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished">Durată activitate</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> + <source>Description</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> + <source>Precedence</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> + <source>Cmdline</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="406"/> + <source>Export rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="407"/> + <source>Import rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="408"/> + <source>Export events to CSV</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> + <source>Quit</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="421"/> + <source>Connections</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished">Conexiuni</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="422"/> + <source>Dropped</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished">Aruncate</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="437"/> + <source>What</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="931"/> + <source>Apply to</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="932"/> + <source>Export</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="942"/> + <source>Reject</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="964"/> + <source>To clipboard</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="965"/> + <source>To disk</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2523"/> + <source>Select a directory to export rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1191"/> + <source> Your are about to delete this entry. </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1678"/> + <source> You are about to delete this node. </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1687"/> + <source><b>Error deleting node</b><br><br></source> + <comment>{0}</comment> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2478"/> + <source>Error exporting rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2552"/> + <source>Select a directory with rules to import (JSON files)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2566"/> + <source>Rules imported fine</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="211"/> + <source>WARNING</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="833"/> + <source>Details</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="834"/> + <source>Rules</source> + <translation type="unfinished">Reguli</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="835"/> + <source>New</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="875"/> + <source>Action</source> + <translation type="unfinished">Acțiune</translation> + </message> +</context> +</TS> diff --git a/ui/i18n/locales/ru_RU/opensnitch-ru_RU.ts b/ui/i18n/locales/ru_RU/opensnitch-ru_RU.ts new file mode 100644 index 0000000..d082939 --- /dev/null +++ b/ui/i18n/locales/ru_RU/opensnitch-ru_RU.ts @@ -0,0 +1,3401 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="ru_RU"> +<context> + <name>Dialog</name> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="34"/> + <source>opensnitch-qt</source> + <translation>opensnitch-qt</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="300"/> + <source>User ID</source> + <translation>ID пользователя</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="334"/> + <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source> + <translation><html><head/><body><p><span style=" font-weight:600;">Выполнено из</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="647"/> + <source>TextLabel</source> + <translation>Заметка</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="437"/> + <source>Source IP</source> + <translation>Исходный IP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="458"/> + <source>Process ID</source> + <translation>ID процесса</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="601"/> + <source>Destination IP</source> + <translation>IP назначения</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="622"/> + <source>Dst Port</source> + <translation>Порт назначения</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="702"/> + <source>from this executable</source> + <translation>из этого исполняемого файла</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="707"/> + <source>from this command line</source> + <translation>из этой командной строки</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="712"/> + <source>this destination port</source> + <translation>этот порт назначения</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="717"/> + <source>this user</source> + <translation>этот пользователь</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="722"/> + <source>this destination ip</source> + <translation>этот ip-адрес назначения</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="751"/> + <source>once</source> + <translation>один раз</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="756"/> + <source>30s</source> + <translation>30 секунд</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="761"/> + <source>5m</source> + <translation>5 минут</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="766"/> + <source>15m</source> + <translation>15 минут</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="771"/> + <source>30m</source> + <translation>30 минут</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="776"/> + <source>1h</source> + <translation>1 час</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="706"/> + <source>for this session</source> + <translation type="obsolete">durante esta sesión</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="786"/> + <source>forever</source> + <translation>постоянно</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="346"/> + <source>Deny</source> + <translation>Запретить</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="337"/> + <source>Allow</source> + <translation>Разрешить</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="865"/> + <source>+</source> + <translation>+</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="781"/> + <source>until reboot</source> + <translation>до перезагрузки</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="727"/> + <source>from this PID</source> + <translation>из этого PIDа</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="809"/> + <source>action</source> + <translation>действие</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="14"/> + <source>Firewall</source> + <translation>Фаервол</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="55"/> + <source><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></source> + <translation><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Фаервол</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="320"/> + <source>Inbound</source> + <translation>Входящие</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="313"/> + <source>Outbound</source> + <translation>Исходящие</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="275"/> + <source>Profile</source> + <translation>Профиль</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="375"/> + <source>Allow inbound connections to a port</source> + <translation>Разрешить входящие соединения на порт</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="378"/> + <source>Allow service (IN)</source> + <translation>Разрешить сервис (ВХОДЯЩИЕ)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="397"/> + <source>Exclude outbound connections to a port from being intercepted</source> + <translation>Исключение исходящих подключений к порту от перехвата</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="406"/> + <source>Allow service (OUT)</source> + <translation>Разрешить сервис (ИСХОДЯЩИЕ)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="426"/> + <source>New rule</source> + <translation>Новое правило</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="453"/> + <source>xxx</source> + <translation type="obsolete">ххх</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="431"/> + <source>Close</source> + <translation>Закрыть</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="14"/> + <source>Firewall rule</source> + <translation>Правило фаервола</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="26"/> + <source>Node</source> + <translation>Узел</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="34"/> + <source>All</source> + <translation type="obsolete">Все</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="38"/> + <source>Enable</source> + <translation>Включить</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="50"/> + <source>Description</source> + <translation>Описание</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="90"/> + <source>Simple</source> + <translation>Просто</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="154"/> + <source>Add new condition</source> + <translation>Добавить новое условие</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="177"/> + <source>Remove selected condition</source> + <translation>Удалить выбранное условие</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="233"/> + <source>Direction</source> + <translation>Направление</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="248"/> + <source>IN</source> + <translation>ВХОДЯЩИЕ</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="257"/> + <source>OUT</source> + <translation>ИСХОДЯЩИЕ</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="223"/> + <source>Action</source> + <translation>Действие</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="285"/> + <source>ACCEPT</source> + <translation>ПРИНИМАТЬ</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="294"/> + <source>DROP</source> + <translation>ОТБРАСЫВАТЬ</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="303"/> + <source>REJECT</source> + <translation>ОТКЛОНЯТЬ</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="312"/> + <source>RETURN</source> + <translation>ВОЗВРАЩАТЬ</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="442"/> + <source>Clear</source> + <translation>Очистить</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="453"/> + <source>Delete</source> + <translation>Удалить</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="464"/> + <source>Save</source> + <translation>Сохранить</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="475"/> + <source>Add</source> + <translation>Добавить</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="266"/> + <source>FORWARD</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="271"/> + <source>PREROUTING</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="276"/> + <source>POSTROUTING</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="321"/> + <source>QUEUE</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="330"/> + <source>DNAT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="335"/> + <source>SNAT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="340"/> + <source>REDIRECT</source> + <translation>ПЕРЕНАПРАВЛЕНИЕ</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="359"/> + <source>depending on the Action (i.e.: target), the syntaxis of the parameters will vary. +Some examples: + +QUEUE -> num 0 (or 1, 2, ...) +REDIRECT, TPROXY, DNAT, SNAT, MASQUERADE: + to :22 + to 192.168.1.254:8080 + to 192.168.1.254 + to 1024-2048 (masquerade)</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>PreferencesDialog</name> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="14"/> + <source>Preferences</source> + <translation>Настройки</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="484"/> + <source>UI</source> + <translation>Пользовательский интерфейс</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="54"/> + <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p></body></html></source> + <translation type="obsolete">Este timeout es la cuenta atrás que aparece cuando se muestra una ventana emergente</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="466"/> + <source>Default timeout</source> + <translation>Тайм-аут по умолчанию</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="340"/> + <source>Pop-up default duration</source> + <translation>Продолжительность всплывающего окна по умолчанию</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="866"/> + <source>Default duration</source> + <translation>Продолжительность по умолчанию</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="162"/> + <source>Pop-up default action</source> + <translation type="obsolete">Acción por defecto de la ventana emergente</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="483"/> + <source>Default action</source> + <translation type="obsolete">Acción por defecto</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="323"/> + <source>Default target</source> + <translation>Цель по умолчанию</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="179"/> + <source>center</source> + <translation>в центре</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="184"/> + <source>top right</source> + <translation>в правом верхнем углу</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="189"/> + <source>bottom right</source> + <translation>в нижнем правом углу</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="194"/> + <source>top left</source> + <translation>в левом верхнем углу</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="199"/> + <source>bottom left</source> + <translation>в нижнем левом углу</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="167"/> + <source>Prompt dialog default position on screen</source> + <translation type="obsolete">Posición por defecto</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="283"/> + <source>by executable</source> + <translation>исполняемым файлом</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="288"/> + <source>by command line</source> + <translation>по командной строке</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="293"/> + <source>by destination port</source> + <translation>по порту назначения</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="298"/> + <source>by destination ip</source> + <translation>по IP-адресу назначения</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="303"/> + <source>by user id</source> + <translation>по ID пользователя</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="970"/> + <source>once</source> + <translation>один раз</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="219"/> + <source>30s</source> + <translation>30 секунд</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="224"/> + <source>5m</source> + <translation>5 минут</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="229"/> + <source>15m</source> + <translation>15 минут</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="234"/> + <source>30m</source> + <translation>30 минут</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="239"/> + <source>1h</source> + <translation>1 час</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="240"/> + <source>for this session</source> + <translation type="obsolete">durante esta sesión</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="249"/> + <source>forever</source> + <translation>постоянно</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1012"/> + <source>deny</source> + <translation>запрещено</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1021"/> + <source>allow</source> + <translation>разрешено</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="406"/> + <source>Disable pop-ups, only display an alert</source> + <translation type="obsolete">Отключить всплывающие окна, отображать только предупреждение</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="823"/> + <source>Nodes</source> + <translation>Узлы</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="829"/> + <source>Process monitor method</source> + <translation>Метод мониторинга процесса</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="863"/> + <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source> + <translation><html><head/><body><p>Продолжительность по умолчанию будет иметь место, когда пользовательский интерфейс не подключен.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="988"/> + <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source> + <translation><html><head/><body><p>Адрес узла.</p><p>По умолчанию: unix:///tmp/osui.sock (unix:// является обязательным, если это Unix сокет) </p><p>Это также может быть IP-адрес с портом: 127.0.0.1:50051</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="991"/> + <source>Address</source> + <translation>Адрес</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1131"/> + <source>Default log level</source> + <translation>Уровень логирования по умолчанию</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1039"/> + <source>Version</source> + <translation>Версия</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="902"/> + <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source> + <translation><html><head/><body><p>Действие по умолчанию выполняется при отсутствии подключенного пользовательского интерфейса.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="846"/> + <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source> + <translation><html><head/><body><p>Лог файл для логирования.<br/></p><p>/dev/stdout выводит логи на стандартный вывод.</p></body> </html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="849"/> + <source>Log file</source> + <translation>Лог файл</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="578"/> + <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons.</p><p>The pop-up dialog will only contain information about the network connection.</p></body></html></source> + <translation type="obsolete">Si marcas esta opción, OpenSnitch te preguntará para Aceptar o Denegar conexiones que no tengan un PID asociado por diferentes razones. + +La ventana emergente sólo contendrá información relativa a la conexión. + +Nota: Estas conexiones no tienen por qué indicar que algo sospechoso está sucediendo. Simplemente +es que no hemos descubierto el PID (por ejemplo conexiones que no se originan en la máquina, o paquetes en mal estado).</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="581"/> + <source>Intercept Unknown Connections</source> + <translation type="obsolete">Interceptar conexiones desconocidas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="921"/> + <source>HostName</source> + <translation>Имя хоста</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1090"/> + <source>unix:///tmp/osui.sock</source> + <translation>unix:///tmp/osui.sock</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="975"/> + <source>until restart</source> + <translation>до перезапуска</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="980"/> + <source>always</source> + <translation>всегда</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1102"/> + <source>/var/log/opensnitchd.log</source> + <translation>/var/log/opensnitchd.log</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1107"/> + <source>/dev/stdout</source> + <translation>/dev/stdout</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="879"/> + <source>Apply configuration to all nodes</source> + <translation>Применить конфигурацию ко всем узлам</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1146"/> + <source>Database</source> + <translation>База данных</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1181"/> + <source>In memory</source> + <translation>В памяти</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1186"/> + <source>File</source> + <translation>Файл</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1482"/> + <source>Close</source> + <translation>Закрыть</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1493"/> + <source>Apply</source> + <translation>Применить</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1504"/> + <source>Save</source> + <translation>Сохранить</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="244"/> + <source>until reboot</source> + <translation>до перезагрузки</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1200"/> + <source>Database type</source> + <translation>Тип базы данных</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1207"/> + <source>Select</source> + <translation>Выбрать</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="83"/> + <source>Pop-ups default options</source> + <translation type="obsolete">Opciones por defecto de las ventanas emergentes</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="367"/> + <source>Pop-ups default position on screen</source> + <translation type="obsolete">Posición en pantalla</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="102"/> + <source><html><head/><body><p>The advanced view allows you to apply more filters on a connection</p><p>when a pop-up appears.</p></body></html></source> + <translation type="obsolete">La vista avanzada permite filtrar conexiones por más parámetros</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="359"/> + <source>Show advanced view by default</source> + <translation>Показывать расширенный вид по умолчанию</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="653"/> + <source>Action</source> + <translation>Действие</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="375"/> + <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source> + <translation><html><head/><body><p>Если этот флажок установлен, всплывающие окна будут отображаться с активным расширенным видом.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="343"/> + <source>Duration</source> + <translation>Длительность</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="263"/> + <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source> + <translation><html><head/><body><p>По умолчанию, когда появляется новое всплывающее окно, в его простейшей форме вы сможете фильтровать соединения или приложения по одному свойству соединения (исполняемый файл, порт, IP-адрес и т. д.).</p><p>С помощью этих вариантов, вы можете выбрать несколько полей для фильтрации подключений.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="266"/> + <source>Filter connections also by:</source> + <translation>Также фильтровать соединения по:</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="362"/> + <source>If checked, this field will be checked when a pop-up is displayed</source> + <translation type="obsolete">Si lo seleccionas, este campo se usará para filtrar las conexiones</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="81"/> + <source>User ID</source> + <translation>ID пользователя</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="97"/> + <source>Destination port</source> + <translation>Порт назначения</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="113"/> + <source>Destination IP</source> + <translation>IP назначения</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="463"/> + <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source> + <translation><html><head/><body><p>Этот тайм-аут представляет собой обратный отсчет, который вы видите, когда отображается всплывающее диалоговое окно.</p><p>Если всплывающее окно не отвечает, будут применены параметры по умолчанию.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="356"/> + <source>The advanced view allows you to easily select multiple fields to filter connections</source> + <translation>Расширенный вид позволяет легко выбирать несколько полей для фильтрации подключений</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="110"/> + <source>If checked, this field will be selected when a pop-up is displayed</source> + <translation>Если флажок установлен, это поле будет выбрано при отображении всплывающего окна</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="159"/> + <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source> + <translation><html><head/><body><p>Действие всплывающего окна по умолчанию.</p><p>Когда будет установлено новое исходящее соединение, это действие будет выбрано по умолчанию, поэтому, если тайм-аут срабатывает , будет применен этот параметр.</p><p><br/></p><p>Когда всплывающее окно просит пользователя разрешить или запретить соединение:</p><p >1. новые исходящие соединения запрещены.</p><p>2. известные соединения разрешаются или запрещаются на основе правил, определенных пользователем.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="905"/> + <source>Default action when the GUI is disconnected</source> + <translation>Обычное действие, когда интерфейс отключен</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1001"/> + <source>Debug invalid connections</source> + <translation>Отладка недействительных соединений</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="39"/> + <source>Pop-ups</source> + <translation>Всплывающие окна</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="64"/> + <source>Default options</source> + <translation>Параметры по умолчанию</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="330"/> + <source>Default position on screen</source> + <translation>Положение по умолчанию на экране</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="769"/> + <source>any temporary rules</source> + <translation>любые временные правила</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="478"/> + <source><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Если выбран этот параметр, правила выбранной продолжительности не будут добавляться в список временных правил в графическом интерфейсе.</p><p><br/></p><p>Временные правила останутся в силе, и вы сможете использовать их, когда будет предложено разрешить/запретить новое подключение.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="481"/> + <source>Don't save rules of duration</source> + <translation type="obsolete">Не сохранять правила длительности</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="463"/> + <source>Show events columns</source> + <translation type="obsolete">Mostrar columnas de la pestaña Eventos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="589"/> + <source>Time</source> + <translation>Время</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="669"/> + <source>Destination</source> + <translation>Назначение</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="637"/> + <source>Protocol</source> + <translation>Протокол</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="685"/> + <source>Process</source> + <translation>Процесс</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="605"/> + <source>Rule</source> + <translation>Правило</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="621"/> + <source>Node</source> + <translation>Узел</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="723"/> + <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using wireguard.</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Eсли этот флажок установлен, opensnitch предложит вам разрешить или запретить соединения, не имеющие связанного PID, по нескольким причинам, в основном из-за плохого состояния соединений.</p> <p>Всплывающее диалоговое окно будет содержать только информацию о сетевом подключении.</p><p>Хотя в некоторых сценариях это действительные подключения, например, при установке VPN с помощью wireguard.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="577"/> + <source>Events tab columns</source> + <translation>Столбцы вкладки "События"</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="308"/> + <source>by PID</source> + <translation>по PIDу</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="496"/> + <source>Desktop notifications</source> + <translation>Уведомления на рабочем столе</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="514"/> + <source>Use system notifications</source> + <translation>Использовать системные уведомления</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="530"/> + <source>Use Qt notifications</source> + <translation>Исользовать уведомления Qt</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="559"/> + <source>Test</source> + <translation>Тест</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="716"/> + <source>System</source> + <translation>Система</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="708"/> + <source>Theme</source> + <translation>Тема</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="998"/> + <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1294"/> + <source>minutes</source> + <translation>минут</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1326"/> + <source>Minutes between events purges</source> + <translation>Минут между очисткой событий</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1352"/> + <source>days</source> + <translation>дни</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1365"/> + <source>Maximum days of events to keep</source> + <translation>Максимальное количество дней для сохранения событий</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="147"/> + <source>reject</source> + <translation>отклонять</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="473"/> + <source>Disable pop-ups, only display a notification</source> + <translation>Запретить всплывающие окна, показывать только уведомления</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="695"/> + <source>Command line</source> + <translation>Командная строка</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="748"/> + <source>Rules</source> + <translation>Правила</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="756"/> + <source>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI. + +Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="761"/> + <source>Don't save/Delete rules of duration</source> + <translation>Не сохранять/Удалить правила по продолжительности</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="779"/> + <source>30s or less</source> + <translation>Не более 30 сек</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="784"/> + <source>5m or less</source> + <translation>Не более 5 мин</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="789"/> + <source>15m or less</source> + <translation>Не более 15 мин</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="794"/> + <source>30m or less</source> + <translation>Не более 30 мин</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="799"/> + <source>1h or less</source> + <translation>Не более 1 часа</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="724"/> + <source>Language</source> + <translation>Язык</translation> + </message> +</context> +<context> + <name>ProcessDetailsDialog</name> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="14"/> + <source>Process details</source> + <translation>Детали процесса</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="61"/> + <source>loading...</source> + <translation>загрузка...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="81"/> + <source>CWD: loading...</source> + <translation>CWD: загрузка...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="93"/> + <source>mem stats: loading...</source> + <translation>статистика памяти: загружается...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="121"/> + <source>Status</source> + <translation>Состояние</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="135"/> + <source>Open files</source> + <translation>Открыть файлы</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="149"/> + <source>I/O Statistics</source> + <translation>I/O Статистика</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="163"/> + <source>Memory mapped files</source> + <translation>Файлы с отображением памяти</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="177"/> + <source>Stack</source> + <translation>Стек</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="191"/> + <source>Environment variables</source> + <translation>Переменные среды</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="210"/> + <source>Application pids</source> + <translation>PIDы приложений</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="240"/> + <source>Start or stop monitoring this process</source> + <translation>Начать или остановить мониторинг этого процесса</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="256"/> + <source>Close</source> + <translation>Закрыть</translation> + </message> +</context> +<context> + <name>RulesDialog</name> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="20"/> + <source>Rule</source> + <translation>Правило</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> + <source>Node</source> + <translation>Узел</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> + <source>Apply rule to all nodes</source> + <translation>Применить правило ко всем узлам</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="379"/> + <source>From this command line</source> + <translation>Из этой командной строки</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="472"/> + <source>From this executable</source> + <translation>Из этого исполняемого файла</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="56"/> + <source>Action</source> + <translation>Действие</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="138"/> + <source>/path/to/executable, .*/bin/executable[0-9\.]+$, ...</source> + <translation type="obsolete">/путь/к/исполняемому/файлу, .*/bin/executable[0-9\.]+$, ...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="610"/> + <source>To this IP / Network</source> + <translation>К этому IP / Сети</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="97"/> + <source>once</source> + <translation>один раз</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="102"/> + <source>30s</source> + <translation type="obsolete">30 секунд</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="107"/> + <source>5m</source> + <translation type="obsolete">5 минут</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="112"/> + <source>15m</source> + <translation type="obsolete">15 минут</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="117"/> + <source>30m</source> + <translation type="obsolete">30 минут</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="122"/> + <source>1h</source> + <translation type="obsolete">1 час</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="230"/> + <source>until restart</source> + <translation type="obsolete">hasta reiniciar (el servicio)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="132"/> + <source>always</source> + <translation>всегда</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="902"/> + <source>To this port</source> + <translation>К этому порту</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="372"/> + <source>From this user ID</source> + <translation>От этого ID пользователя</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/> + <source>Commas or spaces are not allowed to specify multiple domains. + +Use regular expressions instead: +.*(opensnitch|duckduckgo).com +.*\.google.com + +or a single domain: +www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... +gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source> + <translation>Запятые или пробелы не могут указывать несколько доменов. + +Вместо этого используйте регулярные выражения: +.*(opensnitch|duckduckgo).com +.*\.google.com + +или один домен: +www.gnu.org - это будет соответствовать только www.gnu.org, но не ftp.gnu.org, и не www2.gnu.org,... +gnu.org - это будет соответствовать только gnu.org, но не www.gnu.org, и не ftp.gnu.org, ...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="603"/> + <source>www.domain.org, .*\.domain.org</source> + <translation>www.domain.org, .*\.domain.org</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="526"/> + <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source> + <translation><html><head/><body><p>Только TCP, UDP или UDPLITE разрешены</p><p>Вы можете использовать regexp: ^(TCP|UDP)$</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="532"/> + <source>TCP</source> + <translation>TCP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="411"/> + <source>UDP</source> + <translation type="obsolete">UDP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="416"/> + <source>UDPLITE</source> + <translation type="obsolete">UDPLITE</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="421"/> + <source>TCP6</source> + <translation type="obsolete">TCP6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="426"/> + <source>UDP6</source> + <translation type="obsolete">UDP6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="431"/> + <source>UDPLITE6</source> + <translation type="obsolete">UDPLITE6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="760"/> + <source>You can specify a single IP: +- 192.168.1.1 + +or a regular expression: +- 192\.168\.1\.[0-9]+ + +multiple IPs: +- ^(192\.168\.1\.1|172\.16\.0\.1)$ + +You can also specify a subnet: +- 192.168.1.0/24 + +Note: Commas or spaces are not allowed to separate IPs or networks.</source> + <translation>Вы можете указать один IP: +- 192.168.1.1 + +или регулярное выражение: +- 192\.168\.1\.[0-9]+ + +несколько IP-адресов: +- ^(192\.168\.1\.1|172\.16\.0\.1)$ + +Вы также можете указать подсеть: +- 192.168.1.0/24 + +Примечание. Запятые или пробелы не могут использоваться для разделения IP-адресов или сетей.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="532"/> + <source>LAN</source> + <translation type="obsolete">LAN</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="537"/> + <source>127.0.0.0/8</source> + <translation type="obsolete">127.0.0.0/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="542"/> + <source>192.168.0.0/24</source> + <translation type="obsolete">192.168.0.0/24</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="547"/> + <source>192.168.1.0/24</source> + <translation type="obsolete">192.168.1.0/24</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="552"/> + <source>192.168.2.0/24</source> + <translation type="obsolete">192.168.2.0/24</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="557"/> + <source>192.168.0.0/16</source> + <translation type="obsolete">192.168.0.0/16</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="562"/> + <source>169.254.0.0/16</source> + <translation type="obsolete">169.254.0.0/16</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="567"/> + <source>172.16.0.0/12</source> + <translation type="obsolete">172.16.0.0/12</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="572"/> + <source>10.0.0.0/8</source> + <translation type="obsolete">10.0.0.0/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="577"/> + <source>::1/128</source> + <translation type="obsolete">::1/128</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="582"/> + <source>fc00::/7</source> + <translation type="obsolete">fc00::/7</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="587"/> + <source>ff00::/8</source> + <translation type="obsolete">ff00::/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/> + <source>fe80::/10</source> + <translation type="obsolete">fe80::/10</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="597"/> + <source>fd00::/8</source> + <translation type="obsolete">fd00::/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="89"/> + <source>Duration</source> + <translation>Длительность</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="633"/> + <source>Protocol</source> + <translation>Протокол</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="750"/> + <source>To this host</source> + <translation>К этому хосту</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="151"/> + <source>Deny</source> + <translation>Запретить</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="191"/> + <source>Allow</source> + <translation>Разрешить</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="245"/> + <source>Name</source> + <translation type="unfinished">Имя</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="207"/> + <source>Enable</source> + <translation>Включить</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="238"/> + <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them. + +000-allow-localhost +001-deny-broadcast +...</source> + <translation>Правила проверяются в алфавитном порядке, поэтому вы можете назвать их соответствующим образом, чтобы расставить приоритеты. + +000-allow-localhost +001-deny-broadcast +...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="935"/> + <source>leave blank to autocreate</source> + <translation type="obsolete">оставьте пустым для автосоздания</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="214"/> + <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one. + +You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example: + +[x] Priority - 000-priority-rule +[ ] Priority - 001-less-priority-rule</source> + <translation>Если этот флажок установлен, это правило будет иметь приоритет над остальными правилами. Никакие другие правила не будут проверяться после этого. + +Вы должны назвать правило таким образом, чтобы оно проверялось первым, потому что они проверяются в алфавитном порядке. Например: + +[x] Priority - 000-priority-rule +[ ] Priority - 001-less-priority-rule</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="222"/> + <source>Priority rule</source> + <translation>Правило приоритета</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1092"/> + <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source> + <translation><html><head/><body><p>По умолчанию поле правил не чувствительно к регистру, т. е. если процесс пытается получить доступ к gOOgle.CoM, а у вас есть правило Запретить .*google.com, соединение будет заблокировано.<br/></p><p>Если вы установите этот флажок, вы должны указать точную строку (домен, исполняемый файл, командную строку), которую вы хотите отфильтровать.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1095"/> + <source>Case-sensitive</source> + <translation>Чувствительно к регистру</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="686"/> + <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Вы можете указать несколько портов, используя регулярные выражения:</p><p><br/></p><p>- 53, 80 или 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 или 5551, 5552, 5553, итд:</p><p>^(53|443|555[0-9])$</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="127"/> + <source>until reboot</source> + <translation>до перезагрузки</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="980"/> + <source>To this list of domains</source> + <translation>К этому списку доменов</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="539"/> + <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Выберите каталог со списками доменов, которые нужно заблокировать или разрешить.</p><p>Поместите в этот каталог файлы с любым расширением, содержащие списки доменов.</p><p><br/>Формат каждой записи списка следующий (формат хостов):</p><p>127.0.0.1 www.domain.com</p><p>или </p><p>0.0.0.0 www.domain.com</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="148"/> + <source>Deny will just discard the connection</source> + <translation>Отказ мгновенно разорвёт соединение</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="165"/> + <source>Reject will drop the connection, and kill the socket that initiated it</source> + <translation>Отклонение приведет к разрыву соединения и уничтожению сокета, который его инициировал</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="168"/> + <source>Reject</source> + <translation>Отклонить</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="185"/> + <source>Allow will allow the connection</source> + <translation>Разрешить разрешит соединения</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="346"/> + <source>Applications</source> + <translation>Приложения</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="355"/> + <source><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="365"/> + <source>Is regular expression</source> + <translation>Регулярное выражение</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="389"/> + <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="399"/> + <source>From this PID</source> + <translation>Из этого PIDа</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> + <source>is regular expression</source> + <translation>регулярное выражение</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="491"/> + <source>Network</source> + <translation>Сеть</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="932"/> + <source>List of domains/IPs</source> + <translation>Список доменов/IP-адресов</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="938"/> + <source>To this list of network ranges</source> + <translation>К этому списку сетевых диапазонов</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="945"/> + <source>To this list of IPs</source> + <translation>К этому списку IP-адресов</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="971"/> + <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1006"/> + <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1034"/> + <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1049"/> + <source>To this list of domains +(regular expressions)</source> + <translation>К этому списку доменов +(регулярные выражения)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1076"/> + <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1086"/> + <source>More</source> + <translation>Ещё</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="83"/> + <source>Name (leave blank to autocreate)</source> + <translation type="obsolete">Имя (при отсутствии создаётся автоматически)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="566"/> + <source>ICMP</source> + <translation>ICMP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="571"/> + <source>ICMP6</source> + <translation>ICMP6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="576"/> + <source>SCTP</source> + <translation>SCTP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="581"/> + <source>SCTP6</source> + <translation>SCTP6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="863"/> + <source>Network interface</source> + <translation>Сетевой интерфейс</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1102"/> + <source>Don't log connections that match this rule</source> + <translation>Не логировать соединения соотвтсвующие этому правилу</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1105"/> + <source>Don't log connections</source> + <translation>Не логировать соединения</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="937"/> + <source>Color</source> + <translation type="obsolete">Цвет</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1151"/> + <source>Description...</source> + <translation>Описание...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="743"/> + <source>From this IP / Network</source> + <translation>От этого IP или сети</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="872"/> + <source>From this port</source> + <translation>От этого порта</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="918"/> + <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>StatsDialog</name> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="34"/> + <source>OpenSnitch Network Statistics</source> + <translation>Сетевая статистика OpenSnitch</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="284"/> + <source>Save to CSV</source> + <translation type="obsolete">Сохранить в CSV.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="294"/> + <source>Ctrl+S</source> + <translation type="obsolete">Ctrl+S</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="333"/> + <source>Create a new rule</source> + <translation>Создать новое правило</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="376"/> + <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source> + <translation><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="413"/> + <source>Status</source> + <translation>Состояние</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1793"/> + <source>-</source> + <translation>-</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="451"/> + <source>Start or Stop interception</source> + <translation>Начать или остановить перехват</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="496"/> + <source>Events</source> + <translation>События</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="94"/> + <source>Filter</source> + <translation>Фильтр</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="107"/> + <source>Allow</source> + <translation>Разрешить</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="116"/> + <source>Deny</source> + <translation>Запретить</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="143"/> + <source>Ex.: firefox</source> + <translation>Например: firefox</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="205"/> + <source>50</source> + <translation>50</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="210"/> + <source>100</source> + <translation>100</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="215"/> + <source>200</source> + <translation>200</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="220"/> + <source>300</source> + <translation>300</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="826"/> + <source>Nodes</source> + <translation>Узлы</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="554"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Addr column to view details of a node)</span></p></body></html></source> + <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(дважды щелкните столбец Адреса, чтобы просмотреть сведения об узле)</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1700"/> + <source>Rules</source> + <translation>Правила</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="995"/> + <source>enable</source> + <translation>включить</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="684"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Name column to view details of a rule)</span></p></body></html></source> + <translation type="obsolete">(doble click en la columna Nombre para ver los detalles)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="692"/> + <source>search rule name</source> + <translation type="obsolete">искать название правила</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="782"/> + <source>Application rules</source> + <translation>Правила приложений</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="936"/> + <source>Permanent</source> + <translation>Постоянно</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="945"/> + <source>Temporary</source> + <translation>Временно</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1063"/> + <source>Hosts</source> + <translation>Хосты</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1364"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click to view details of an item)</span></p></body></html></source> + <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(дважды щелкните, чтобы просмотреть сведения об элементе)</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1153"/> + <source>Applications</source> + <translation>Приложения</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1266"/> + <source>Addresses</source> + <translation>Адреса</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1356"/> + <source>Ports</source> + <translation>Порты</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1440"/> + <source>Users</source> + <translation>Пользователи</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1544"/> + <source>Connections</source> + <translation>Соединения</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1596"/> + <source>Dropped</source> + <translation>Сброшено</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1648"/> + <source>Uptime</source> + <translation>Время работы</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1767"/> + <source>Version</source> + <translation>Версия</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="233"/> + <source>Delete all intercepted events</source> + <translation>Удалить все перехваченные события</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1022"/> + <source>Edit rule</source> + <translation>Редактировать правило</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1039"/> + <source>Delete rule</source> + <translation>Удалить правило</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="926"/> + <source>Delete all intercepted hosts</source> + <translation type="obsolete">Удалить всю информацию о перехваченных хостах</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1051"/> + <source>Delete all intercepted applications</source> + <translation type="obsolete">Удалить всю информацию о перехваченных приложениях</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1159"/> + <source>Delete all intercepted addresses</source> + <translation type="obsolete">Удалить всю информацию о перехваченных адресах</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1261"/> + <source>Delete all intercepted ports</source> + <translation type="obsolete">Удалить всю информацию о перехваченных портах</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1371"/> + <source>Delete all intercepted users</source> + <translation type="obsolete">Удалить всю информацию о перехваченных пользователях</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="699"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click on a row to view details of a rule)</span></p></body></html></source> + <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(дважды щелкните строку, чтобы просмотреть сведения о правиле)</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="665"/> + <source>Delete connections that matched this rule</source> + <translation type="obsolete">Удалить подключения, соответствующие этому правилу</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="927"/> + <source>All applications</source> + <translation>Все приложения</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="125"/> + <source>Reject</source> + <translation>Отклонить</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="180"/> + <source>0</source> + <translation>0</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="637"/> + <source>Delete this node</source> + <translation>Удалить этот узел</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="653"/> + <source>Show the preferences of this node</source> + <translation>Показать настройки этого узла</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="669"/> + <source>Start or stop interception of this node</source> + <translation>Запустить или остановить перехват этого узла</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="777"/> + <source>2</source> + <translation>2</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="954"/> + <source>System rules</source> + <translation>Системные правила</translation> + </message> +</context> +<context> + <name>contextual_menu</name> + <message> + <location filename="../../../opensnitch/service.py" line="47"/> + <source>Statistics</source> + <translation>Статистика</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="50"/> + <source>Help</source> + <translation>Помощь</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="51"/> + <source>Close</source> + <translation>Закрыть</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="48"/> + <source>Enable</source> + <translation>Включить</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="49"/> + <source>Disable</source> + <translation>Выключить</translation> + </message> +</context> +<context> + <name>firewall</name> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="91"/> + <source>Configuration applied.</source> + <translation>Конфигурация применена.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="404"/> + <source>Error: {0}</source> + <translation>Ошибка: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="193"/> + <source>Applying changes...</source> + <translation>Применить изменения...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="230"/> + <source>Error getting INPUT chain policy</source> + <translation>Ошибка получения политики цепочки INPUT</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="237"/> + <source>Error getting OUTPUT chain policy</source> + <translation>Ошибка получения политики цепочки OUTPUT</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="290"/> + <source>In order to configure firewall rules from the GUI, we need to use 'nftables' instead of 'iptables'</source> + <translation>Чтобы настроить правила фаервола из графического интерфейса, нам нужно использовать 'nftables' вместо 'iptables'</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="304"/> + <source>Enabling firewall...</source> + <translation>Включение фаервола...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="306"/> + <source>Disabling firewall...</source> + <translation>Выключение фервола...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="71"/> + <source>Dest Port</source> + <translation>Порт назначения</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="72"/> + <source>Source Port</source> + <translation>Исходный порт</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="73"/> + <source>Dest IP</source> + <translation>IP-адрес назначения</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="74"/> + <source>Source IP</source> + <translation>Исходный IP</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="75"/> + <source>Input interface</source> + <translation>Входной интерфейс</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="76"/> + <source>Output interface</source> + <translation>Выходной интерфейс</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="77"/> + <source>Set conntrack mark</source> + <translation>Установить метку соединения</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="78"/> + <source>Match conntrack mark</source> + <translation>Совпадение с меткой conntrack</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="79"/> + <source>Match conntrack state(s)</source> + <translation>Совпадение с состоянием соединения</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="80"/> + <source>Set mark on packet</source> + <translation>Установить метку на пакете</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="81"/> + <source>Match packet information</source> + <translation>Совпадение информации о пакете</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="87"/> + <source>Bandwidth quotas</source> + <translation>Квоты пропускной способности</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="89"/> + <source>Rate limit connections</source> + <translation>Соединения с ограничением скорости</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="373"/> + <source>Your protobuf version is incompatible, you need to install protobuf 3.8.0 or superior +(pip3 install --ignore-installed protobuf==3.8.0)</source> + <translation>Ваша версия protobuf несовместима, необходимо установить protobuf 3.8.0 или выше +(pip3 install --ignore-installed protobuf==3.8.0)</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="397"/> + <source>Rule deleted</source> + <translation>Правило удалено</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="401"/> + <source>Rule added</source> + <translation>Правило добавлено</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="420"/> + <source>You can use ',' or '-' to specify multiple ports/IPs or ranges/values:<br><br>ports: 22 or 22,443 or 50000-60000<br>IPs: 192.168.1.1 or 192.168.1.30-192.168.1.130<br>Values: echo-reply,echo-request<br>Values: new,established,related</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="440"/> + <source>Deleting rule, wait</source> + <translation>Удаляем правило, подождите</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="443"/> + <source>Error updating rule</source> + <translation>Не удалось обновить правило</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="483"/> + <source>Adding rule, wait</source> + <translation>Добавляем правило, подождите</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="492"/> + <source><select a statement></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="787"/> + <source>Equal</source> + <translation>Равно</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="788"/> + <source>Not equal</source> + <translation>Не равно</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="789"/> + <source>Greater or equal than</source> + <translation>Больше или равно чем</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="790"/> + <source>Greater than</source> + <translation>Больше чем</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="791"/> + <source>Less or equal than</source> + <translation>Меньше или равно чем</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="792"/> + <source>Less than</source> + <translation>Меньше чем</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1350"/> + <source>Firewall rule</source> + <translation>Правило фаервола</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="885"/> + <source>Simple</source> + <translation>Просто</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="890"/> + <source>Advanced</source> + <translation>Сложно</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1046"/> + <source>This rule is not supported yet.</source> + <translation>Это правило пока не поддерживается.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1111"/> + <source>Exclude service</source> + <translation>Исключить сервис</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1123"/> + <source>Allow inbound connections to the selected port.</source> + <translation>Разрешить входящие подключения к выбранному порту.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1125"/> + <source>Allow outbound connections to the selected port.</source> + <translation>Разрешить исходящие подключения к выбранному порту.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1201"/> + <source>select a statement.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1217"/> + <source>value cannot be 0 or empty.</source> + <translation>значение не может быть 0 или пустым.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1229"/> + <source>the value format is 1024/kbytes (or bytes, mbytes, gbytes)</source> + <translation>формат значения: 1024/kbytes (или bytes, mbytes, gbytes)</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1240"/> + <source>the value format is 1024/kbytes/second (or bytes, mbytes, gbytes)</source> + <translation>формат значения: 1024/kbytes/секунд (или bytes, mbytes, gbytes)</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1243"/> + <source>rate-limit not valid, use: bytes, kbytes, mbytes or gbytes.</source> + <translation>ограничение скорости недействительно, используйте: bytes, kbytes, mbytes или gbytes.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1245"/> + <source>time-limit not valid, use: second, minute, hour or day</source> + <translation>ограничение по времени недействительно, используйте: секунды, минуты, часы или дни</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1293"/> + <source>port not valid.</source> + <translation>порт недействителен.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="108"/> + <source> +Supported formats: + + - Simple: 23 + - Ranges: 80-1024 + - Multiple ports: 80,443,8080 +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="134"/> + <source> +Supported formats: + + - Simple: 1.2.3.4 + - IP ranges: 1.2.3.100-1.2.3.200 + - Network ranges: 1.2.3.4/24 +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="147"/> + <source>Match input interface. Regular expressions not allowed.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="154"/> + <source>Match output interface. Regular expressions not allowed.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="161"/> + <source>Set a conntrack mark on the connection, in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="171"/> + <source>Match a conntrack mark of the connection, in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="178"/> + <source>Match conntrack states. + +Supported formats: + - Simple: new + - Multiple states separated by commas: related,new +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="193"/> + <source> +Match packet's metainformation. + +Value must be in decimal format, except for the "l4proto" option. +For l4proto it can be a lower case string, for example: + tcp + udp + icmp, + etc + +If the value is decimal for protocol or lproto, it'll use it as the code of +that protocol. +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="213"/> + <source>Set a mark on the packet matching the specified conditions. The value is in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="221"/> + <source> +Match ICMP codes. + +Supported formats: + - Simple: echo-request + - Multiple separated by commas: echo-request,echo-reply +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="234"/> + <source> +Match ICMPv6 codes. + +Supported formats: + - Simple: echo-request + - Multiple separated by commas: echo-request,echo-reply +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="247"/> + <source>Print a message when this rule matches a packet.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="254"/> + <source> +Apply quotas on connections. + +For example when: + - "quota over 10/mbytes" -> apply the Action defined (DROP) + - "quota until 10/mbytes" -> apply the Action defined (ACCEPT) + +The value must be in the format: VALUE/UNITS, for example: + - 10mbytes, 1/gbytes, etc +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="286"/> + <source> +Apply limits on connections. + +For example when: + - "limit over 10/mbytes/minute" -> apply the Action defined (DROP, ACCEPT, etc) + (When there're more than 10MB per minute, apply an Action) + + - "limit until 10/mbytes/hour" -> apply the Action defined (ACCEPT) + +The value must be in the format: VALUE/UNITS/TIME, for example: + - 10/mbytes/minute, 1/gbytes/hour, etc +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="607"/> + <source>num</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="621"/> + <source>to</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>menu_close</name> + <message> + <location filename="../../../opensnitch/service.py" line="131"/> + <source>Close</source> + <translation type="obsolete">Cerrar</translation> + </message> +</context> +<context> + <name>menu_help</name> + <message> + <location filename="../../../opensnitch/service.py" line="126"/> + <source>Help</source> + <translation type="obsolete">Ayuda</translation> + </message> +</context> +<context> + <name>menu_statistics</name> + <message> + <location filename="../../../opensnitch/service.py" line="120"/> + <source>Statistics</source> + <translation type="obsolete">Eventos</translation> + </message> +</context> +<context> + <name>messages</name> + <message> + <location filename="../../../opensnitch/service.py" line="281"/> + <source>Info</source> + <translation>Информация</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="285"/> + <source>Error</source> + <translation>Ошибка</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="289"/> + <source>Warning</source> + <translation>Предупреждение</translation> + </message> +</context> +<context> + <name>notifications</name> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="654"/> + <source>System notifications are not available, you need to install python3-notify2.</source> + <translation>Системные уведомления недоступны, нужно установить python3-notify2.</translation> + </message> +</context> +<context> + <name>popups</name> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="115"/> + <source>Allow</source> + <translation>Разрешить</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="114"/> + <source>Deny</source> + <translation>Запретить</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/> + <source>forever</source> + <translation>навсегда</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="331"/> + <source>Outgoing connection</source> + <translation>Исходящее соединение</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="336"/> + <source>Process launched from:</source> + <translation>Процесс запущен из:</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="377"/> + <source>from this command line</source> + <translation>из этой командной строки</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="373"/> + <source>from this executable</source> + <translation>из этого исполняемого файла</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="208"/> + <source>Unknown process</source> + <translation type="obsolete">Proceso no encontrado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="50"/> + <source>until reboot</source> + <translation>до перезагрузки</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="379"/> + <source>to port {0}</source> + <translation>в порт {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="222"/> + <source><b>%s</b> is connecting to <b>%s</b> on %s port %d</source> + <translation type="obsolete"><b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="228"/> + <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source> + <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="442"/> + <source>to {0}</source> + <translation>в {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="382"/> + <source>from user {0}</source> + <translation>от пользователя {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="399"/> + <source>to {0}.*</source> + <translation>в {0}.*</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="452"/> + <source>to *.{0}</source> + <translation>в *.{0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="378"/> + <source>to *{0}</source> + <translation type="obsolete">в *{0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="486"/> + <source><b>Remote</b> process %s running on <b>%s</b></source> + <translation><b>Удаленный</b> процесс %s запущенный на <b>%s</b></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="490"/> + <source>is connecting to <b>%s</b> on %s port %d</source> + <translation>подключается к <b>%s</b> через %s порт %d</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="502"/> + <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source> + <translation>пытается разрешить <b>%s</b> через %s, %s порт %d</translation> + </message> + <message> + <location filename="../../../opensnitch/notifications.py" line="122"/> + <source>New outgoing connection</source> + <translation>Новое исходящее соединение</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="386"/> + <source>from this PID</source> + <translation>из этого PIDа</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="116"/> + <source>Reject</source> + <translation>Отклонять</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="497"/> + <source>is connecting to <b>%s</b>, %s</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/notifications.py" line="42"/> + <source>Open</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>popups2</name> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="254"/> + <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source> + <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> + </message> +</context> +<context> + <name>preferences</name> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="171"/> + <source>Exception saving config: %s</source> + <translation type="obsolete">Error al guarda la configuración: %s</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="177"/> + <source>Applying configuration on %s ...</source> + <translation type="obsolete">Aplicando configuración en %s ...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="292"/> + <source>Server address can not be empty</source> + <translation>Адрес сервера не может быть пустым</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="227"/> + <source>Error loading %s configuration</source> + <translation type="obsolete">Error al cargar la configuración %s</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="568"/> + <source>Configuration applied.</source> + <translation>Конфигурация применена.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="257"/> + <source>Error applying configuration: %s</source> + <translation type="obsolete">Error al aplicar la configuración: %s</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="386"/> + <source>Exception saving config: {0}</source> + <translation>Конфигурация сохранения исключений: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="500"/> + <source>Applying configuration on {0} ...</source> + <translation>Применение конфигурации к {0}...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="321"/> + <source>Error loading {0} configuration</source> + <translation>Ошибка при загрузке конфигурации {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="570"/> + <source>Error applying configuration: {0}</source> + <translation>Ошибка применения конфигурации: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> + <source>Warning</source> + <translation>Предупреждение</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> + <source>You must select a file for the database<br>or choose "In memory" type.</source> + <translation>Вы должны выбрать файл для базы данных<br>или выбрать тип "В памяти".</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="401"/> + <source>DB type changed</source> + <translation>Тип БД изменен</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="38"/> + <source>Restart the GUI in order effects to take effect</source> + <translation>Перезапустите графический интерфейс, чтобы эффекты вступили в силу</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="607"/> + <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source> + <translation>Наведите указатель мыши на текст, чтобы отобразить справку<br><br>Не забудьте посетить вики: <a href="{0}">{0}</a></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="466"/> + <source>System</source> + <translation>Система</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="187"/> + <source>Themes not available. Install qt-material: pip3 install qt-material</source> + <translation>Темы недоступны. Установите qt-material: pip3 install qt-material</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> + <source>UI theme changed</source> + <translation>Тема оформления изменена</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> + <source>Restart the GUI in order to apply the new theme</source> + <translation>Перезапустите графический интерфейс, чтобы изменить тему оформления</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="388"/> + <source>There're no nodes connected</source> + <translation>Нет подключенных узлов</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="508"/> + <source>Ok</source> + <translation>Хорошо</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="472"/> + <source>Restart the GUI in order changes to take effect</source> + <translation type="obsolete">Перезапустите графический интерфейс, чтобы изменения вступили в силу</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="520"/> + <source>Exception saving node config {0}: {1}</source> + <translation>Исключение сохранения конфигурации узла {0}: {1}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="164"/> + <source>System default</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="433"/> + <source>Language changed</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>proc_details</name> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="100"/> + <source><b>Error loading process information:</b> <br><br> + +</source> + <translation><b>Ошибка при загрузке информации о процессе:</b> <br><br> + +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="119"/> + <source><b>Error stopping monitoring process:</b><br><br></source> + <translation><b>Ошибка при остановке мониторинга процесса:</b><br><br></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="159"/> + <source>loading...</source> + <translation>загрузка...</translation> + </message> +</context> +<context> + <name>rules</name> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="228"/> + <source>There're no nodes connected.</source> + <translation>Нет подключенных узлов.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="271"/> + <source>Rule applied.</source> + <translation>Правило применено.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="123"/> + <source>Error applying rule: %s</source> + <translation type="obsolete">Error al aplicar la regla: %s</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="641"/> + <source>protocol can not be empty, or uncheck it</source> + <translation>протокол не может быть пустым, или снимите галочку</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="655"/> + <source>Protocol regexp error</source> + <translation>Ошибка регулярного выражения протокола</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="659"/> + <source>process path can not be empty</source> + <translation>путь процесса не может быть пустым</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="673"/> + <source>Process path regexp error</source> + <translation>Ошибка регулярного выражения пути процесса</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="677"/> + <source>command line can not be empty</source> + <translation>командная строка не может быть пустой</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="691"/> + <source>Command line regexp error</source> + <translation>Ошибка регулярного выражения командной строки</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="731"/> + <source>Dest port can not be empty</source> + <translation>Порт назначения не может быть пустым</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="745"/> + <source>Dst port regexp error</source> + <translation>Ошибка регулярного выражения порта назначения</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="749"/> + <source>Dest host can not be empty</source> + <translation>Хост назначения не может быть пустым</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="763"/> + <source>Dst host regexp error</source> + <translation>Ошибка регулярного выражения хоста назначения</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="805"/> + <source>Dest IP/Network can not be empty</source> + <translation>Целевой IP/сеть не могут быть пустыми</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="831"/> + <source>Dst IP regexp error</source> + <translation>Ошибка регулярного выражения Целевой IP</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="843"/> + <source>User ID can not be empty</source> + <translation>Укажите идентификатор пользователя</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="857"/> + <source>User ID regexp error</source> + <translation>Ошибка регулярного выражения ID пользователя</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="273"/> + <source>Error applying rule: {0}</source> + <translation>Ошибка применения правила: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="931"/> + <source>Lists field cannot be empty</source> + <translation>Поле списков не может быть пустым</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="933"/> + <source>Lists field must be a directory</source> + <translation>Поле списков должно быть каталогом</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="976"/> + <source><b>Rule not supported</b></source> + <translation><b>Правило не поддерживается</b></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="539"/> + <source><b>Error loading rule</b></source> + <translation><b>Ошибка загрузки правила</b></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="245"/> + <source>There's already a rule with this name.</source> + <translation>Правило с таким названием уже существует.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="861"/> + <source>PID field can not be empty</source> + <translation>Поле PID не может быть пустым</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="875"/> + <source>PID field regexp error</source> + <translation>Ошибка регулярного выражения поля PID</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="963"/> + <source>Select at least one field.</source> + <translation>Выберите хотя бы одно поле.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="695"/> + <source>Network interface can not be empty</source> + <translation>Название сетевого интерфейса не может быть пустым</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="709"/> + <source>Network interface regexp error</source> + <translation>Ошибка регулярного выражения сетевого интерфейса</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="713"/> + <source>Source port can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="727"/> + <source>Source port regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="767"/> + <source>Source IP/Network can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="793"/> + <source>Source IP regexp error</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>stats</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="313"/> + <source>Not running</source> + <translation>Не запущено</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="314"/> + <source>Disabled</source> + <translation>Отключено</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="315"/> + <source>Running</source> + <translation>Запущено</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="412"/> + <source>OpenSnitch Network Statistics</source> + <translation type="obsolete">Eventos de OpenSnitch</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="414"/> + <source>OpenSnitch Network Statistics for</source> + <translation type="obsolete">Eventos de OpenSnitch de</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1189"/> + <source> Your are about to delete this rule. </source> + <translation> Вы собираетесь удалить это правило. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> + <source> Are you sure?</source> + <translation> Вы уверены?</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="636"/> + <source>OpenSnitch Network Statistics {0}</source> + <translation>OpenSnitch статистика сети {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="638"/> + <source>OpenSnitch Network Statistics for {0}</source> + <translation>OpenSnitch статистика сети для {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> + <source>Name</source> + <translation type="obsolete">Nombre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Address</source> + <translation type="obsolete">Dirección</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> + <source>Status</source> + <translation type="obsolete">Estado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> + <source>Hostname</source> + <translation type="obsolete">Hostname</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> + <source>Version</source> + <translation type="obsolete">Versión</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="834"/> + <source>Rules</source> + <translation type="unfinished">Reglas</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Time</source> + <translation type="obsolete">Hora</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="875"/> + <source>Action</source> + <translation type="unfinished">Acción</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> + <source>Duration</source> + <translation type="obsolete">Duración</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> + <source>Node</source> + <translation type="obsolete">Nodo</translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="18"/> + <source>Hits</source> + <translation>Попадания</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>Protocol</source> + <translation type="obsolete">Protocolo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2581"/> + <source>Save as CSV</source> + <translation>Сохранить как CSV</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Enabled</source> + <translation type="obsolete">Habilitado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="961"/> + <source>Delete</source> + <translation>Удалить</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="948"/> + <source>always</source> + <translation type="obsolete">siempre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="580"/> + <source><b>Error:</b><br><br>{0}</source> + <translation type="obsolete"><b>Error:</b><br><br>{0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="954"/> + <source>Disable</source> + <translation>Отключить</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="956"/> + <source>Enable</source> + <translation>Включить</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="959"/> + <source>Duplicate</source> + <translation>Дублировать</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="960"/> + <source>Edit</source> + <translation>Редактировать</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1248"/> + <source>Rule not found by that name and node</source> + <translation>Правило не найдено по этому имени и узлу</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1301"/> + <source><b>Error:</b><br><br></source> + <comment>{0}</comment> + <translation><b>Ошибка:</b><br><br></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1308"/> + <source>Warning:</source> + <translation>Предупреждение:</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="940"/> + <source>Allow</source> + <translation>Разрешить</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="941"/> + <source>Deny</source> + <translation>Запретить</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="945"/> + <source>Always</source> + <translation>Всегда</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="946"/> + <source>Until reboot</source> + <translation>До перезагрузки</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> + <source> You are about to delete this rule. </source> + <translation> Вы собираетесь удалить это правило. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> + <source>Process</source> + <translation type="obsolete">Aplicación</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> + <source>Destination</source> + <translation type="obsolete">Destino</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> + <source>Rule</source> + <translation type="obsolete">Regla</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> + <source>UserID</source> + <translation type="obsolete">UserID</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> + <source>LastConnection</source> + <translation type="obsolete">Última Conexión</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> + <source>Name</source> + <comment>xxxxx</comment> + <translation type="obsolete">Nombre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> + <source>Name</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Nombre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Address</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Dirección</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> + <source>Status</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Estado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> + <source>Hostname</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Hostname</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> + <source>Version</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Versión</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> + <source>Rules</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Reglas</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Time</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Hora</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> + <source>Action</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Acción</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> + <source>Duration</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Duración</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> + <source>Node</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Nodo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Enabled</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Habilitado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> + <source>Hits</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Total</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>Protocol</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Protocolo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> + <source>Process</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Aplicación</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> + <source>Destination</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Destino</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> + <source>Rule</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Regla</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> + <source>UserID</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">UserID</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> + <source>LastConnection</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">ÚltimaConexión</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="287"/> + <source>Name</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Имя</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="288"/> + <source>Address</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Адрес</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="289"/> + <source>Status</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Состояние</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="290"/> + <source>Hostname</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Имя хоста</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="423"/> + <source>Version</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Версия</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="420"/> + <source>Rules</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Правила</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Time</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Время</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> + <source>Action</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Действие</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> + <source>Duration</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Продолжительность</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> + <source>Node</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Узел</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Enabled</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Включено</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="438"/> + <source>Hits</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Посещаемость</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> + <source>Protocol</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Протокол</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Process</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Процесс</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>Destination</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Назначение</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> + <source>Rule</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Правило</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> + <source>UserID</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>ID пользователя</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> + <source>LastConnection</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Последнее соединение</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> + <source>Args</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="obsolete">Аргументы</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> + <source>DstIP</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>IP назначения</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> + <source>DstHost</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Хост назначения</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> + <source>DstPort</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Порт назначения</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="776"/> + <source>New node connected</source> + <translation>Подключен новый узел</translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="17"/> + <source>What</source> + <translation>Что</translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="19"/> + <source>Network name</source> + <translation>Имя сети</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="291"/> + <source>Uptime</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Время работы</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> + <source>Precedence</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Приоритет</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="421"/> + <source>Connections</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Соединения</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="422"/> + <source>Dropped</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Сброшено</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="437"/> + <source>What</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Что</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="931"/> + <source>Apply to</source> + <translation>Применить к</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="942"/> + <source>Reject</source> + <translation>Отклонить</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> + <source>Description</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Описание</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> + <source>Cmdline</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Командная строка</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="406"/> + <source>Export rules</source> + <translation>Экспортировать правила</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="407"/> + <source>Import rules</source> + <translation>Импортиовать правила</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="408"/> + <source>Export events to CSV</source> + <translation>Экспортиовать события в CSV</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> + <source>Quit</source> + <translation>Выйти</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="932"/> + <source>Export</source> + <translation>Экспортировать</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="964"/> + <source>To clipboard</source> + <translation>В буфер обмена</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="965"/> + <source>To disk</source> + <translation>На диск</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2523"/> + <source>Select a directory to export rules</source> + <translation>Выберите директорию для экспортирования правил</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1191"/> + <source> Your are about to delete this entry. </source> + <translation> Вы собираетесь удалить эту запись. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1678"/> + <source> You are about to delete this node. </source> + <translation> Вы собираетесь удалить этот узел. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1687"/> + <source><b>Error deleting node</b><br><br></source> + <comment>{0}</comment> + <translation><b>Ошибка удаления узла</b><br><br></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2478"/> + <source>Error exporting rules</source> + <translation>Ошибка экспорта правил</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2552"/> + <source>Select a directory with rules to import (JSON files)</source> + <translation>Выберите директорию для импорта (JSON) файлов с правилами</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2566"/> + <source>Rules imported fine</source> + <translation>Правила импортированы нормально</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="211"/> + <source>WARNING</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="833"/> + <source>Details</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="835"/> + <source>New</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>stats_deleterule</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="774"/> + <source> Your are about to delete this rule. </source> + <translation type="obsolete"> Estás a punto de borrar esta regla. </translation> + </message> +</context> +<context> + <name>stats_deleterule2</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="776"/> + <source> Are you sure?</source> + <translation type="obsolete"> ¿Estás seguro?</translation> + </message> +</context> +<context> + <name>stats_disabled</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="74"/> + <source>Disabled</source> + <translation type="obsolete">Deshabilitado</translation> + </message> +</context> +<context> + <name>stats_notrunning</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="73"/> + <source>Not running</source> + <translation type="obsolete">Parado</translation> + </message> +</context> +<context> + <name>stats_running</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="75"/> + <source>Running</source> + <translation type="obsolete">Interceptando</translation> + </message> +</context> +<context> + <name>stats_wintitle</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> + <source>OpenSnitch Network Statistics</source> + <translation type="obsolete">Eventos de red OpenSnitch</translation> + </message> +</context> +<context> + <name>stats_wintitle2</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="411"/> + <source>OpenSnitch Network Statistics for</source> + <translation type="obsolete">Eventos de OpenSnitch de</translation> + </message> +</context> +</TS> diff --git a/ui/i18n/locales/tr_TR/opensnitch-tr_TR.ts b/ui/i18n/locales/tr_TR/opensnitch-tr_TR.ts new file mode 100644 index 0000000..bb15095 --- /dev/null +++ b/ui/i18n/locales/tr_TR/opensnitch-tr_TR.ts @@ -0,0 +1,3408 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="tr"> +<context> + <name>Dialog</name> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="34"/> + <source>opensnitch-qt</source> + <translation>opensnitch-qt</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="300"/> + <source>User ID</source> + <translation>Kullanıcı Kimliği</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="334"/> + <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source> + <translation><html><head/><body><p><span style=" font-weight:600;">Şuradan çalıştırıldı</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="647"/> + <source>TextLabel</source> + <translation>TextLabel</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="437"/> + <source>Source IP</source> + <translation>Kaynak IP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="458"/> + <source>Process ID</source> + <translation>İşlem Kimliği</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="601"/> + <source>Destination IP</source> + <translation>Hedef IP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="622"/> + <source>Dst Port</source> + <translation>Hedef Bağlantı Noktası</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="702"/> + <source>from this executable</source> + <translation>bu programdan</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="707"/> + <source>from this command line</source> + <translation>bu komut satırından</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="712"/> + <source>this destination port</source> + <translation>bu hedef bağlantı noktası</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="717"/> + <source>this user</source> + <translation>bu kullanıcı</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="722"/> + <source>this destination ip</source> + <translation>bu hedef IP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="751"/> + <source>once</source> + <translation>bir kere</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="756"/> + <source>30s</source> + <translation>30sn</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="761"/> + <source>5m</source> + <translation>5dak</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="766"/> + <source>15m</source> + <translation>15dak</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="771"/> + <source>30m</source> + <translation>30dak</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="776"/> + <source>1h</source> + <translation>1sa</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="706"/> + <source>for this session</source> + <translation type="obsolete">durante esta sesión</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="786"/> + <source>forever</source> + <translation>sonsuza kadar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="346"/> + <source>Deny</source> + <translation>Reddet</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="337"/> + <source>Allow</source> + <translation>İzin ver</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="865"/> + <source>+</source> + <translation>+</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="781"/> + <source>until reboot</source> + <translation>yeniden başlatılana kadar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="727"/> + <source>from this PID</source> + <translation>bu işlem kimliğinden</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="809"/> + <source>action</source> + <translation>eylem</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="14"/> + <source>Firewall</source> + <translation>Güvenlik duvarı</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="55"/> + <source><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></source> + <translation><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Güvenlik duvarı</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="320"/> + <source>Inbound</source> + <translation>Gelen</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="313"/> + <source>Outbound</source> + <translation>Giden</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="275"/> + <source>Profile</source> + <translation>Profil</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="375"/> + <source>Allow inbound connections to a port</source> + <translation>Bir bağlantı noktasına gelen bağlantılara izin ver</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="378"/> + <source>Allow service (IN)</source> + <translation>Hizmete izin ver (GELEN)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="397"/> + <source>Exclude outbound connections to a port from being intercepted</source> + <translation>Bir bağlantı noktasına giden bağlantıları araya girmekten hariç tut</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="406"/> + <source>Allow service (OUT)</source> + <translation>Hizmete izin ver (GİDEN)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="426"/> + <source>New rule</source> + <translation>Yeni kural</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="453"/> + <source>xxx</source> + <translation type="obsolete">xxx</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="431"/> + <source>Close</source> + <translation>Kapat</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="14"/> + <source>Firewall rule</source> + <translation>Güvenlik duvarı kuralı</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="26"/> + <source>Node</source> + <translation>Düğüm</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="34"/> + <source>All</source> + <translation type="obsolete">Tümü</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="38"/> + <source>Enable</source> + <translation>Etkinleştir</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="50"/> + <source>Description</source> + <translation>Açıklama</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="90"/> + <source>Simple</source> + <translation>Basit</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="154"/> + <source>Add new condition</source> + <translation>Yeni koşul ekle</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="177"/> + <source>Remove selected condition</source> + <translation>Seçilen koşulu kaldır</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="233"/> + <source>Direction</source> + <translation>Yön</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="248"/> + <source>IN</source> + <translation>GELEN</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="257"/> + <source>OUT</source> + <translation>GİDEN</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="223"/> + <source>Action</source> + <translation>Eylem</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="285"/> + <source>ACCEPT</source> + <translation>KABUL ET</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="294"/> + <source>DROP</source> + <translation>BIRAK</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="303"/> + <source>REJECT</source> + <translation>GERİ ÇEVİR</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="312"/> + <source>RETURN</source> + <translation>GERİ DÖN</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="442"/> + <source>Clear</source> + <translation>Temizle</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="453"/> + <source>Delete</source> + <translation>Sil</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="464"/> + <source>Save</source> + <translation>Kaydet</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="475"/> + <source>Add</source> + <translation>Ekle</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="266"/> + <source>FORWARD</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="271"/> + <source>PREROUTING</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="276"/> + <source>POSTROUTING</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="321"/> + <source>QUEUE</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="330"/> + <source>DNAT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="335"/> + <source>SNAT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="340"/> + <source>REDIRECT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="359"/> + <source>depending on the Action (i.e.: target), the syntaxis of the parameters will vary. +Some examples: + +QUEUE -> num 0 (or 1, 2, ...) +REDIRECT, TPROXY, DNAT, SNAT, MASQUERADE: + to :22 + to 192.168.1.254:8080 + to 192.168.1.254 + to 1024-2048 (masquerade)</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>PreferencesDialog</name> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="14"/> + <source>Preferences</source> + <translation>Tercihler</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="484"/> + <source>UI</source> + <translation>Kullanıcı arayüzü</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="54"/> + <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p></body></html></source> + <translation type="obsolete">Este timeout es la cuenta atrás que aparece cuando se muestra una ventana emergente</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="466"/> + <source>Default timeout</source> + <translation>Öntanımlı zaman aşımı</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="340"/> + <source>Pop-up default duration</source> + <translation>Öntanımlı açılır pencere süresi</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="866"/> + <source>Default duration</source> + <translation>Öntanımlı süre</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="162"/> + <source>Pop-up default action</source> + <translation type="obsolete">Acción por defecto de la ventana emergente</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="483"/> + <source>Default action</source> + <translation type="obsolete">Acción por defecto</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="323"/> + <source>Default target</source> + <translation>Öntanımlı hedef</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="179"/> + <source>center</source> + <translation>merkez</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="184"/> + <source>top right</source> + <translation>sağ üst</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="189"/> + <source>bottom right</source> + <translation>sağ alt</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="194"/> + <source>top left</source> + <translation>sol üst</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="199"/> + <source>bottom left</source> + <translation>sol alt</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="167"/> + <source>Prompt dialog default position on screen</source> + <translation type="obsolete">Posición por defecto</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="283"/> + <source>by executable</source> + <translation>programa göre</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="288"/> + <source>by command line</source> + <translation>komut satırına göre</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="293"/> + <source>by destination port</source> + <translation>hedef bağlantı noktasına göre</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="298"/> + <source>by destination ip</source> + <translation>hedef IP'ye göre</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="303"/> + <source>by user id</source> + <translation>kullanıcı kimliğine göre</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="970"/> + <source>once</source> + <translation>bir kere</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="219"/> + <source>30s</source> + <translation>30sn</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="224"/> + <source>5m</source> + <translation>5dak</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="229"/> + <source>15m</source> + <translation>15dak</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="234"/> + <source>30m</source> + <translation>30dak</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="239"/> + <source>1h</source> + <translation>1sa</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="240"/> + <source>for this session</source> + <translation type="obsolete">durante esta sesión</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="249"/> + <source>forever</source> + <translation>sonsuza kadar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1012"/> + <source>deny</source> + <translation>reddet</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1021"/> + <source>allow</source> + <translation>izin ver</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="406"/> + <source>Disable pop-ups, only display an alert</source> + <translation type="obsolete">Açılır pencereleri devre dışı bırak, yalnızca bir uyarı görüntüle</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="823"/> + <source>Nodes</source> + <translation>Düğümler</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="829"/> + <source>Process monitor method</source> + <translation>İşlem izleme yöntemi</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="863"/> + <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source> + <translation><html><head/><body><p>Öntanımlı süre, bağlı bir kullanıcı arayüzü olmadığında gerçekleşecektir.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="988"/> + <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source> + <translation><html><head/><body><p>Düğümün adresi.</p><p>Öntanımlı: unix:///tmp/osui.sock (Unix soketi ise unix:// zorunludur)</p><p>Bağlantı noktası ile birlikte bir IP adresi de olabilir: 127.0.0.1:50051</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="991"/> + <source>Address</source> + <translation>Adres</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1131"/> + <source>Default log level</source> + <translation>Öntanımlı günlük kaydı düzeyi</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1039"/> + <source>Version</source> + <translation>Sürüm</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="902"/> + <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source> + <translation><html><head/><body><p>Öntanımlı eylem, bağlı bir kullanıcı arayüzü olmadığında gerçekleşecektir.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="846"/> + <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source> + <translation><html><head/><body><p>Günlük kayıtlarının yazılacağı günlük dosyası.<br/></p><p>/dev/stdout standart çıktıya yazdıracaktır.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="849"/> + <source>Log file</source> + <translation>Günlük kaydı dosyası</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="578"/> + <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons.</p><p>The pop-up dialog will only contain information about the network connection.</p></body></html></source> + <translation type="obsolete">Si marcas esta opción, OpenSnitch te preguntará para Aceptar o Denegar conexiones que no tengan un PID asociado por diferentes razones. + +La ventana emergente sólo contendrá información relativa a la conexión. + +Nota: Estas conexiones no tienen por qué indicar que algo sospechoso está sucediendo. Simplemente +es que no hemos descubierto el PID (por ejemplo conexiones que no se originan en la máquina, o paquetes en mal estado).</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="581"/> + <source>Intercept Unknown Connections</source> + <translation type="obsolete">Interceptar conexiones desconocidas</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="921"/> + <source>HostName</source> + <translation>Ana makine adı</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1090"/> + <source>unix:///tmp/osui.sock</source> + <translation>unix:///tmp/osui.sock</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="975"/> + <source>until restart</source> + <translation>yeniden başlatılana kadar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="980"/> + <source>always</source> + <translation>her zaman</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1102"/> + <source>/var/log/opensnitchd.log</source> + <translation>/var/log/opensnitchd.log</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1107"/> + <source>/dev/stdout</source> + <translation>/dev/stdout</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="879"/> + <source>Apply configuration to all nodes</source> + <translation>Yapılandırmayı tüm düğümlere uygula</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1146"/> + <source>Database</source> + <translation>Veri tabanı</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1181"/> + <source>In memory</source> + <translation>Bellekte</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1186"/> + <source>File</source> + <translation>Dosya</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1482"/> + <source>Close</source> + <translation>Kapat</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1493"/> + <source>Apply</source> + <translation>Uygula</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1504"/> + <source>Save</source> + <translation>Kaydet</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="244"/> + <source>until reboot</source> + <translation>yeniden başlatılana kadar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1200"/> + <source>Database type</source> + <translation>Veri tabanı türü</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1207"/> + <source>Select</source> + <translation>Seç</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="83"/> + <source>Pop-ups default options</source> + <translation type="obsolete">Opciones por defecto de las ventanas emergentes</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="367"/> + <source>Pop-ups default position on screen</source> + <translation type="obsolete">Posición en pantalla</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="102"/> + <source><html><head/><body><p>The advanced view allows you to apply more filters on a connection</p><p>when a pop-up appears.</p></body></html></source> + <translation type="obsolete">La vista avanzada permite filtrar conexiones por más parámetros</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="359"/> + <source>Show advanced view by default</source> + <translation>Öntanımlı olarak gelişmiş görünümü göster</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="653"/> + <source>Action</source> + <translation>Eylem</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="375"/> + <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source> + <translation><html><head/><body><p>İşaretlenirse, açılır pencereler gelişmiş görünüm etkinken görüntülenecektir.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="343"/> + <source>Duration</source> + <translation>Süre</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="263"/> + <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source> + <translation><html><head/><body><p>Öntanımlı olarak, yeni bir açılır pencere göründüğünde, en basit haliyle, bağlantıları veya uygulamaları bağlantının bir özelliğine göre (program, bağlantı noktası, IP, vb.) filtreleyebileceksiniz.</p><p>Bu seçeneklerle, bağlantıları filtrelemek için birden fazla alan seçebilirsiniz.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="266"/> + <source>Filter connections also by:</source> + <translation>Bağlantıları şuna göre de filtrele:</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="362"/> + <source>If checked, this field will be checked when a pop-up is displayed</source> + <translation type="obsolete">Si lo seleccionas, este campo se usará para filtrar las conexiones</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="81"/> + <source>User ID</source> + <translation>Kullanıcı kimliği</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="97"/> + <source>Destination port</source> + <translation>Hedef bağlantı noktası</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="113"/> + <source>Destination IP</source> + <translation>Hedef IP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="463"/> + <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source> + <translation><html><head/><body><p>Bu zaman aşımı, bir açılır iletişim kutusu gösterildiğinde gördüğünüz geri sayımdır.</p><p>Açılır pencereye yanıt verilmezse, öntanımlı seçenekler uygulanacaktır.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="356"/> + <source>The advanced view allows you to easily select multiple fields to filter connections</source> + <translation>Gelişmiş görünüm, bağlantıları filtrelemek için birden fazla alanı kolayca seçmenize olanak tanır</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="110"/> + <source>If checked, this field will be selected when a pop-up is displayed</source> + <translation>İşaretlenirse, bir açılır pencere görüntülendiğinde bu alan seçilecektir</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="159"/> + <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source> + <translation><html><head/><body><p>Açılır pencere öntanımlı eylemi.</p><p>Yeni bir giden bağlantı kurulmak üzereyken, bu eylem öntanımlı olarak seçilecektir, bu nedenle zaman aşımı devreye girerse, uygulanacak seçenek budur.</p><p><br/></p><p>Bir açılır pencere kullanıcıdan bir bağlantıya izin vermesini veya reddetmesini isterken:</p><p>1. yeni giden bağlantılar reddedilir.</p><p>2. bilinen bağlantılara kullanıcı tarafından tanımlanan kurallara göre izin verilir veya reddedilir.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="905"/> + <source>Default action when the GUI is disconnected</source> + <translation>GUI bağlantısı kesildiğinde öntanımlı eylem</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1001"/> + <source>Debug invalid connections</source> + <translation>Geçersiz bağlantılarda hata ayıkla</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="39"/> + <source>Pop-ups</source> + <translation>Açılır pencereler</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="64"/> + <source>Default options</source> + <translation>Öntanımlı seçenekler</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="330"/> + <source>Default position on screen</source> + <translation>Ekranda öntanımlı konum</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="769"/> + <source>any temporary rules</source> + <translation>herhangi bir geçici kural</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="478"/> + <source><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Bu seçenek seçildiğinde, seçilen sürenin kuralları grafiksel kullanıcı arayüzündeki geçici kurallar listesine eklenmeyecektir.</p><p><br/></p><p>Geçici kurallar hala geçerli olacaktır ve yeni bir bağlantıya izin vermeniz/reddetmeniz istendiğinde bunları kullanabilirsiniz.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="481"/> + <source>Don't save rules of duration</source> + <translation type="obsolete">Süre kurallarını kaydetme</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="463"/> + <source>Show events columns</source> + <translation type="obsolete">Mostrar columnas de la pestaña Eventos</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="589"/> + <source>Time</source> + <translation>Zaman</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="669"/> + <source>Destination</source> + <translation>Hedef</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="637"/> + <source>Protocol</source> + <translation>İletişim kuralı</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="685"/> + <source>Process</source> + <translation>İşlem</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="605"/> + <source>Rule</source> + <translation>Kural</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="621"/> + <source>Node</source> + <translation>Düğüm</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="723"/> + <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using wireguard.</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>İşaretlenirse, opensnitch, çoğunlukla kötü durumdaki bağlantılar olmak üzere çeşitli nedenlerden dolayı, atanmış bir işlem kimliğine sahip olmayan bağlantılara izin vermenizi veya reddetmenizi isteyecektir.</p><p>Açılır iletişim kutusu yalnızca ağ bağlantısı hakkında bilgi içerecektir.</p><p>Yine de, örneğin wireguard kullanarak bir VPN kurarken olduğu gibi, bunların geçerli bağlantılar olduğu bazı durumlar vardır.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="577"/> + <source>Events tab columns</source> + <translation>Olaylar sekmesi sütunları</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="308"/> + <source>by PID</source> + <translation>işlem kimliğine göre</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="461"/> + <source>Disable pop-ups, only display an notification</source> + <translation type="obsolete">Açılır pencereleri devre dışı bırak, yalnızca bir bildirim görüntüle</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="496"/> + <source>Desktop notifications</source> + <translation>Masaüstü bildirimleri</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="514"/> + <source>Use system notifications</source> + <translation>Sistem bildirimlerini kullan</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="530"/> + <source>Use Qt notifications</source> + <translation>Qt bildirimlerini kullan</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="559"/> + <source>Test</source> + <translation>Test</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="716"/> + <source>System</source> + <translation>Sistem</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="708"/> + <source>Theme</source> + <translation>Tema</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="998"/> + <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source> + <translation><html><head/><body><p>İşaretlenirse OpenSnitch, çoğunlukla kötü durumdaki bağlantılar olmak üzere çeşitli nedenlerden dolayı ilişkili bir işlem kimliğine sahip olmayan bağlantılara izin vermenizi veya reddetmenizi isteyecektir.</p><p>Açılır iletişim kutusu yalnızca ağ bağlantısı hakkında bilgi içerecektir.</p><p>WireGuard kullanarak bir VPN kurarken olduğu gibi, bunların geçerli bağlantılar olduğu bazı senaryolar vardır.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1294"/> + <source>minutes</source> + <translation>dakika</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1326"/> + <source>Minutes between events purges</source> + <translation>Olay temizlemeleri arasındaki dakikalar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1352"/> + <source>days</source> + <translation>gün</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1365"/> + <source>Maximum days of events to keep</source> + <translation>Saklanacak azami etkinlik günü sayısı</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="147"/> + <source>reject</source> + <translation>geri çevir</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="473"/> + <source>Disable pop-ups, only display a notification</source> + <translation>Açılır pencereleri devre dışı bırak, yalnızca bir bildirim görüntüle</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="695"/> + <source>Command line</source> + <translation>Komut satırı</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="748"/> + <source>Rules</source> + <translation>Kurallar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="756"/> + <source>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI. + +Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</source> + <translation>Bu seçenek seçildiğinde, seçilen sürenin kuralları grafiksel arayüzdeki geçici kurallar listesine eklenmeyecektir. + +Geçici kurallar geçerli olmaya devam edecektir ve yeni bir bağlantıya izin vermeniz/vermemeniz istendiğinde bunları kullanabilirsiniz.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="761"/> + <source>Don't save/Delete rules of duration</source> + <translation>Süre kurallarını kaydetme/silme</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="779"/> + <source>30s or less</source> + <translation>30sn veya daha az</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="784"/> + <source>5m or less</source> + <translation>5dak veya daha az</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="789"/> + <source>15m or less</source> + <translation>15dak veya daha az</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="794"/> + <source>30m or less</source> + <translation>30dak veya daha az</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="799"/> + <source>1h or less</source> + <translation>1sa veya daha az</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="724"/> + <source>Language</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>ProcessDetailsDialog</name> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="14"/> + <source>Process details</source> + <translation>İşlem ayrıntıları</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="61"/> + <source>loading...</source> + <translation>yükleniyor...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="81"/> + <source>CWD: loading...</source> + <translation>CWD: yükleniyor...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="93"/> + <source>mem stats: loading...</source> + <translation>bellek istatistikleri: yükleniyor...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="121"/> + <source>Status</source> + <translation>Durum</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="135"/> + <source>Open files</source> + <translation>Açık dosyalar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="149"/> + <source>I/O Statistics</source> + <translation>G/Ç İstatistikleri</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="163"/> + <source>Memory mapped files</source> + <translation>Bellek eşlemeli dosyalar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="177"/> + <source>Stack</source> + <translation>Yığın</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="191"/> + <source>Environment variables</source> + <translation>Ortam değişkenleri</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="210"/> + <source>Application pids</source> + <translation>Uygulama işlem kimlikleri</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="240"/> + <source>Start or stop monitoring this process</source> + <translation>Bu işlemi izlemeyi başlat veya durdur</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="256"/> + <source>Close</source> + <translation>Kapat</translation> + </message> +</context> +<context> + <name>RulesDialog</name> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="20"/> + <source>Rule</source> + <translation>Kural</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> + <source>Node</source> + <translation>Düğüm</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> + <source>Apply rule to all nodes</source> + <translation>Kuralı tüm düğümlere uygula</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="379"/> + <source>From this command line</source> + <translation>Bu komut satırından</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="472"/> + <source>From this executable</source> + <translation>Bu programdan</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="56"/> + <source>Action</source> + <translation>Eylem</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="138"/> + <source>/path/to/executable, .*/bin/executable[0-9\.]+$, ...</source> + <translation type="obsolete">/programın/yolu, .*/bin/program[0-9\.]+$, ...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="610"/> + <source>To this IP / Network</source> + <translation>Bu IP'ye / Ağa</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="97"/> + <source>once</source> + <translation>bir kere</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="102"/> + <source>30s</source> + <translation type="obsolete">30sn</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="107"/> + <source>5m</source> + <translation type="obsolete">5dak</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="112"/> + <source>15m</source> + <translation type="obsolete">15dak</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="117"/> + <source>30m</source> + <translation type="obsolete">30dak</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="122"/> + <source>1h</source> + <translation type="obsolete">1sa</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="230"/> + <source>until restart</source> + <translation type="obsolete">hasta reiniciar (el servicio)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="132"/> + <source>always</source> + <translation>her zaman</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="902"/> + <source>To this port</source> + <translation>Bu bağlantı noktasına</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="372"/> + <source>From this user ID</source> + <translation>Bu kullanıcı kimliğinden</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/> + <source>Commas or spaces are not allowed to specify multiple domains. + +Use regular expressions instead: +.*(opensnitch|duckduckgo).com +.*\.google.com + +or a single domain: +www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... +gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source> + <translation>Birden fazla etki alanı belirtmek için virgül veya boşluk kullanılmasına izin verilmez. + +Bunun yerine düzenli ifadeler kullanın: +.*(opensnitch|duckduckgo).com +.*\.google.com + +veya tek bir etki alanı: +www.gnu.org - yalnızca www.gnu.org ile eşleşir, ftp.gnu.org, www2.gnu.org vb. ile eşleşmez. +gnu.org - yalnızca gnu.org ile eşleşir, www.gnu.org, ftp.gnu.org vb. ile eşleşmez.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="603"/> + <source>www.domain.org, .*\.domain.org</source> + <translation>www.etkialani.org, .*\.etkialani.org</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="526"/> + <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source> + <translation><html><head/><body><p>Yalnızca TCP, UDP veya UDPLITE izin verilir</p><p>Düzenli ifade kullanabilirsiniz, örn: ^(TCP|UDP)$</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="532"/> + <source>TCP</source> + <translation>TCP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="411"/> + <source>UDP</source> + <translation type="obsolete">UDP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="416"/> + <source>UDPLITE</source> + <translation type="obsolete">UDPLITE</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="421"/> + <source>TCP6</source> + <translation type="obsolete">TCP6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="426"/> + <source>UDP6</source> + <translation type="obsolete">UDP6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="431"/> + <source>UDPLITE6</source> + <translation type="obsolete">UDPLITE6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="760"/> + <source>You can specify a single IP: +- 192.168.1.1 + +or a regular expression: +- 192\.168\.1\.[0-9]+ + +multiple IPs: +- ^(192\.168\.1\.1|172\.16\.0\.1)$ + +You can also specify a subnet: +- 192.168.1.0/24 + +Note: Commas or spaces are not allowed to separate IPs or networks.</source> + <translation>Tek bir IP belirtebilirsiniz: +- 192.168.1.1 + +veya düzenli bir ifade: +- 192\.168\.1\.[0-9]+ + +birden fazla IP: +- ^(192\.168\.1\.1|172\.16\.0\.1)$ + +Ayrıca bir alt ağ da belirtebilirsiniz: +- 192.168.1.0/24 + +Not: IP veya ağları ayırmak için virgüllere veya boşluklara izin verilmez.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="532"/> + <source>LAN</source> + <translation type="obsolete">LAN</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="537"/> + <source>127.0.0.0/8</source> + <translation type="obsolete">127.0.0.0/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="542"/> + <source>192.168.0.0/24</source> + <translation type="obsolete">192.168.0.0/24</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="547"/> + <source>192.168.1.0/24</source> + <translation type="obsolete">192.168.1.0/24</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="552"/> + <source>192.168.2.0/24</source> + <translation type="obsolete">192.168.2.0/24</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="557"/> + <source>192.168.0.0/16</source> + <translation type="obsolete">192.168.0.0/16</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="562"/> + <source>169.254.0.0/16</source> + <translation type="obsolete">169.254.0.0/16</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="567"/> + <source>172.16.0.0/12</source> + <translation type="obsolete">172.16.0.0/12</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="572"/> + <source>10.0.0.0/8</source> + <translation type="obsolete">10.0.0.0/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="577"/> + <source>::1/128</source> + <translation type="obsolete">::1/128</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="582"/> + <source>fc00::/7</source> + <translation type="obsolete">fc00::/7</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="587"/> + <source>ff00::/8</source> + <translation type="obsolete">ff00::/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/> + <source>fe80::/10</source> + <translation type="obsolete">fe80::/10</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="597"/> + <source>fd00::/8</source> + <translation type="obsolete">fd00::/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="89"/> + <source>Duration</source> + <translation>Süre</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="633"/> + <source>Protocol</source> + <translation>İletişim kuralı</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="750"/> + <source>To this host</source> + <translation>Bu ana makineye</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="151"/> + <source>Deny</source> + <translation>Reddet</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="191"/> + <source>Allow</source> + <translation>İzin ver</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="245"/> + <source>Name</source> + <translation type="unfinished">Ad</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="207"/> + <source>Enable</source> + <translation>Etkinleştir</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="238"/> + <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them. + +000-allow-localhost +001-deny-broadcast +...</source> + <translation>Kurallar alfabetik sıraya göre denetlenir, böylece onları önceliklendirmek için uygun şekilde adlandırabilirsiniz. + +000-localhost-izinver +001-genelyayin-reddet +...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="935"/> + <source>leave blank to autocreate</source> + <translation type="obsolete">otomatik oluşturmak için boş bırakın</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="214"/> + <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one. + +You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example: + +[x] Priority - 000-priority-rule +[ ] Priority - 001-less-priority-rule</source> + <translation>İşaretlenirse, bu kural diğer kurallara göre öncelikli olacaktır. Bundan sonra başka hiçbir kural denetlenmeyecektir. + +Alfabetik sıraya göre denetlendikleri için kuralı önce denetlenecek şekilde adlandırmalısınız. Örneğin: + +[x] Öncelik - 000-oncelik-kurali +[ ] Öncelik - 001-daha-az-oncelik-kurali</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="222"/> + <source>Priority rule</source> + <translation>Öncelik kuralı</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1092"/> + <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source> + <translation><html><head/><body><p>Öntanımlı olarak, kuralların alanı büyük/küçük harfe duyarsızdır, yani bir işlem gOOgle.CoM adresine erişmeye çalışırsa ve .*google.com adresini Reddetmek için bir kuralınız varsa, bağlantı engellenecektir.<br/></p><p>Bu kutuyu işaretlerseniz, filtrelemek istediğiniz dizgeyi (etki alanı, program, komut satırı) tam olarak belirtmeniz gerekir.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1095"/> + <source>Case-sensitive</source> + <translation>Büyük/küçük harfe duyarlı</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="686"/> + <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Düzenli ifadeler kullanarak birden fazla bağlantı noktası belirtebilirsiniz:</p><p><br/></p> <p> - 53, 80 veya 443:</p><p>^(53|80|443)$</p><p><br/></p> <p> - 53, 443 veya 5551, 5552, 5553, vs:</p><p>^(53|443|555[0-9])$</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="127"/> + <source>until reboot</source> + <translation>yeniden başlatılana kadar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="980"/> + <source>To this list of domains</source> + <translation>Bu etki alanı listesine</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="539"/> + <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></source> + <translation type="obsolete"><html><head/><body><p>Engellenecek veya izin verilecek etki alanlarının listelerini içeren bir dizin seçin.</p><p>Etki alanı listelerini içeren herhangi bir uzantıya sahip dosyaları bu dizinin içine yerleştirin.</p><p><br/>Bir listenin her girdisinin biçimi şu şekildedir (hosts dosyası biçimi):</p><p>127.0.0.1 www.etkialani.com</p><p>veya </p><p>0.0.0.0 www.etkialani.com</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="148"/> + <source>Deny will just discard the connection</source> + <translation>Reddet seçeneği yalnızca bağlantıyı iptal edecektir</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="165"/> + <source>Reject will drop the connection, and kill the socket that initiated it</source> + <translation>Geri çevir seçeneği bağlantıyı kesecek ve onu başlatan soketi sonlandıracaktır</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="168"/> + <source>Reject</source> + <translation>Geri çevir</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="185"/> + <source>Allow will allow the connection</source> + <translation>İzin ver seçeneği bağlantıya izin verecektir</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="346"/> + <source>Applications</source> + <translation>Uygulamalar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="355"/> + <source><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></source> + <translation><html><head/><body><p>Bu alanın değeri her zaman program dosyasının mutlak yoludur: /programın/yolu<br/></p><p>Örnekler:</p><p>- Basit: /programın/yolu</p><p>- Birden çok yol: ^/usr/lib(64|)/firefox/firefox$</p><p>- Birden fazla program: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- /tmp'den çalıştırmaları reddet/izin ver:</p><p>^/(var/|)tmp/.*$<br/></p><p>Daha fazla örnek için <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki sayfasını</a> ziyaret edin veya <a href="https://github.com/evilsocket/opensnitch/discussions">Tartışma forumlarında</a> sorun.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="365"/> + <source>Is regular expression</source> + <translation>Düzenli ifadedir</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="389"/> + <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source> + <translation><html><head/><body><p>Bu alan kullanıcı tarafından çalıştırılan komut satırını içerecek ve eşleşecektir.<br/></p><p>Kullanıcı komutu yazdıysa, yalnızca komut görünecektir:</p><p>telnet 1.2.3.4<br/></p><p>Kullanıcı komutun mutlak veya göreceli yolunu yazdıysa, görünecek olan budur:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="399"/> + <source>From this PID</source> + <translation>Bu işlem kimliğinden</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/> + <source>is regular expression</source> + <translation>düzenli ifadedir</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="491"/> + <source>Network</source> + <translation>Ağ</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="932"/> + <source>List of domains/IPs</source> + <translation>Etki alanlarının/IP'lerin listesi</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="938"/> + <source>To this list of network ranges</source> + <translation>Bu ağ aralıkları listesine</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="945"/> + <source>To this list of IPs</source> + <translation>Bu IP listesine</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="971"/> + <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation><html><head/><body><p>Engellenecek veya izin verilecek IP'lerin listesini içeren dosyaların bulunduğu bir dizin seçin:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>vb.</p><p>Satır başına bir IP. Boş veya # ile başlayan satırlar dikkate alınmaz.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1006"/> + <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation><html><head/><body><p>Engellenecek veya izin verilecek ağ aralıklarının listesini içeren dosyaların bulunduğu bir dizin seçin:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>vb.<br/></p><p>Satır başına bir ağ aralığı. Boş veya # ile başlayan satırlar dikkate alınmaz.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1034"/> + <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source> + <translation><html><head/><body><p>Engellenecek veya izin verilecek etki alanlarının listelerini içeren bir dizin seçin.</p><p>Etki alanı listelerini içeren herhangi bir uzantıya sahip dosyaları bu dizinin içine yerleştirin.</p><p><br/>Bir listenin her girdisinin biçimi şu şekildedir (hosts dosyası biçimi):</p><p>127.0.0.1 www.etkialani.com</p><p>veya </p><p>0.0.0.0 www.etkialani.com</p><p>Boş veya # ile başlayan satırlar dikkate alınmaz.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1049"/> + <source>To this list of domains +(regular expressions)</source> + <translation>Bu etki alanları listesine +(düzenli ifadeler)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1076"/> + <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation><html><head/><body><p>Engellenecek veya izin verilecek etki alanlarının düzenli ifadelerini içeren dosyaları içeren bir dizin seçin:</p><p>.*\.ornek\.com</p><p>Bir etki alanını olduğu gibi de kullanabilirsiniz: &quot;ornek.com&quot;, bu herhangibirsey.ornek.com, herhangibirsey.ornek.com.localdomain, vb. ile eşleşecektir.</p><p>Satır başına bir etki alanı. Boş veya # ile başlayan satırlar dikkate alınmaz.</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1086"/> + <source>More</source> + <translation>Daha fazla</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="83"/> + <source>Name (leave blank to autocreate)</source> + <translation type="obsolete">Ad (otomatik oluşturmak için boş bırakın)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="566"/> + <source>ICMP</source> + <translation>ICMP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="571"/> + <source>ICMP6</source> + <translation>ICMP6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="576"/> + <source>SCTP</source> + <translation>SCTP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="581"/> + <source>SCTP6</source> + <translation>SCTP6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="863"/> + <source>Network interface</source> + <translation>Ağ arayüzü</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1102"/> + <source>Don't log connections that match this rule</source> + <translation>Bu kuralla eşleşen bağlantıları günlüğe kaydetme</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1105"/> + <source>Don't log connections</source> + <translation>Bağlantıları günlüğe kaydetme</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="937"/> + <source>Color</source> + <translation type="obsolete">Renk</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1151"/> + <source>Description...</source> + <translation>Açıklama...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="743"/> + <source>From this IP / Network</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="872"/> + <source>From this port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="918"/> + <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>StatsDialog</name> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="34"/> + <source>OpenSnitch Network Statistics</source> + <translation>OpenSnitch Ağ İstatistikleri</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="284"/> + <source>Save to CSV</source> + <translation type="obsolete">CSV olarak kaydet.</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="294"/> + <source>Ctrl+S</source> + <translation type="obsolete">Ctrl+S</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="333"/> + <source>Create a new rule</source> + <translation>Yeni bir kural oluştur</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="376"/> + <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source> + <translation><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">ana makine adı - 192.168.1.1</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="413"/> + <source>Status</source> + <translation>Durum</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1793"/> + <source>-</source> + <translation>-</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="451"/> + <source>Start or Stop interception</source> + <translation>Araya girmeyi başlat veya durdur</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="496"/> + <source>Events</source> + <translation>Olaylar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="94"/> + <source>Filter</source> + <translation>Filtrele</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="107"/> + <source>Allow</source> + <translation>İzin ver</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="116"/> + <source>Deny</source> + <translation>Reddet</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="143"/> + <source>Ex.: firefox</source> + <translation>Örn: firefox</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="205"/> + <source>50</source> + <translation>50</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="210"/> + <source>100</source> + <translation>100</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="215"/> + <source>200</source> + <translation>200</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="220"/> + <source>300</source> + <translation>300</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="826"/> + <source>Nodes</source> + <translation>Düğümler</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="554"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Addr column to view details of a node)</span></p></body></html></source> + <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(bir düğümün ayrıntılarını görüntülemek için Adres sütununa çift tıklayın)</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1700"/> + <source>Rules</source> + <translation>Kurallar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="995"/> + <source>enable</source> + <translation>etkinleştir</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="684"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Name column to view details of a rule)</span></p></body></html></source> + <translation type="obsolete">(doble click en la columna Nombre para ver los detalles)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="692"/> + <source>search rule name</source> + <translation type="obsolete">kural adı ara</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="782"/> + <source>Application rules</source> + <translation>Uygulama kuralları</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="936"/> + <source>Permanent</source> + <translation>Kalıcı</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="945"/> + <source>Temporary</source> + <translation>Geçici</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1063"/> + <source>Hosts</source> + <translation>Ana makineler</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1364"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click to view details of an item)</span></p></body></html></source> + <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(bir ögenin ayrıntılarını görüntülemek için çift tıklayın)</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1153"/> + <source>Applications</source> + <translation>Uygulamalar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1266"/> + <source>Addresses</source> + <translation>Adresler</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1356"/> + <source>Ports</source> + <translation>Bağlantı noktaları</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1440"/> + <source>Users</source> + <translation>Kullanıcılar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1544"/> + <source>Connections</source> + <translation>Bağlantılar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1596"/> + <source>Dropped</source> + <translation>Bırakıldı</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1648"/> + <source>Uptime</source> + <translation>Çalışma süresi</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1767"/> + <source>Version</source> + <translation>Sürüm</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="233"/> + <source>Delete all intercepted events</source> + <translation>Araya girilen tüm olayları sil</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1022"/> + <source>Edit rule</source> + <translation>Kuralı düzenle</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1039"/> + <source>Delete rule</source> + <translation>Kuralı sil</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="926"/> + <source>Delete all intercepted hosts</source> + <translation type="obsolete">Araya girilen tüm ana makineleri sil</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1051"/> + <source>Delete all intercepted applications</source> + <translation type="obsolete">Araya girilen tüm uygulamaları sil</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1159"/> + <source>Delete all intercepted addresses</source> + <translation type="obsolete">Araya girilen tüm adresleri sil</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1261"/> + <source>Delete all intercepted ports</source> + <translation type="obsolete">Araya girilen tüm bağlantı noktalarını sil</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1371"/> + <source>Delete all intercepted users</source> + <translation type="obsolete">Araya girilen tüm kullanıcıları sil</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="699"/> + <source><html><head/><body><p><span style=" font-size:7pt;">(double click on a row to view details of a rule)</span></p></body></html></source> + <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(bir kuralın ayrıntılarını görüntülemek için bir satıra çift tıklayın)</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="665"/> + <source>Delete connections that matched this rule</source> + <translation type="obsolete">Bu kuralla eşleşen bağlantıları sil</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="927"/> + <source>All applications</source> + <translation>Tüm uygulamalar</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="125"/> + <source>Reject</source> + <translation>Geri çevir</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="180"/> + <source>0</source> + <translation>0</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="637"/> + <source>Delete this node</source> + <translation>Bu düğümü sil</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="653"/> + <source>Show the preferences of this node</source> + <translation>Bu düğümün tercihlerini göster</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="669"/> + <source>Start or stop interception of this node</source> + <translation>Bu düğümün araya girmesini başlat veya durdur</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="777"/> + <source>2</source> + <translation>2</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="954"/> + <source>System rules</source> + <translation>Sistem kuralları</translation> + </message> +</context> +<context> + <name>contextual_menu</name> + <message> + <location filename="../../../opensnitch/service.py" line="47"/> + <source>Statistics</source> + <translation>İstatistikler</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="50"/> + <source>Help</source> + <translation>Yardım</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="51"/> + <source>Close</source> + <translation>Kapat</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="48"/> + <source>Enable</source> + <translation>Etkinleştir</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="49"/> + <source>Disable</source> + <translation>Devre dışı bırak</translation> + </message> +</context> +<context> + <name>firewall</name> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="91"/> + <source>Configuration applied.</source> + <translation>Yapılandırma uygulandı.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="404"/> + <source>Error: {0}</source> + <translation>Hata: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="193"/> + <source>Applying changes...</source> + <translation>Değişiklikler uygulanıyor...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="230"/> + <source>Error getting INPUT chain policy</source> + <translation>INPUT zinciri politikası alınırken hata oluştu</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="237"/> + <source>Error getting OUTPUT chain policy</source> + <translation>OUTPUT zinciri politikası alınırken hata oluştu</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="290"/> + <source>In order to configure firewall rules from the GUI, we need to use 'nftables' instead of 'iptables'</source> + <translation>Grafiksel arayüzden güvenlik duvarı kurallarını yapılandırmak için 'iptables' yerine 'nftables' kullanmamız gerekir</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="304"/> + <source>Enabling firewall...</source> + <translation>Güvenlik duvarı etkinleştiriliyor...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="306"/> + <source>Disabling firewall...</source> + <translation>Güvenlik duvarı devre dışı bırakılıyor...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="71"/> + <source>Dest Port</source> + <translation>Hedef Bağlantı Noktası</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="72"/> + <source>Source Port</source> + <translation>Kaynak Bağlantı Noktası</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="73"/> + <source>Dest IP</source> + <translation>Hedef IP</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="74"/> + <source>Source IP</source> + <translation>Kaynak IP</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="75"/> + <source>Input interface</source> + <translation>Giriş arayüzü</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="76"/> + <source>Output interface</source> + <translation>Çıkış arayüzü</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="77"/> + <source>Set conntrack mark</source> + <translation>conntrack işaretini ayarla</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="78"/> + <source>Match conntrack mark</source> + <translation>conntrack işaretini eşleştir</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="79"/> + <source>Match conntrack state(s)</source> + <translation>conntrack durum(lar)ını eşleştir</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="80"/> + <source>Set mark on packet</source> + <translation>Paket üzerinde işareti ayarla</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="81"/> + <source>Match packet information</source> + <translation>Paket bilgilerini eşleştir</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="87"/> + <source>Bandwidth quotas</source> + <translation>Bant genişliği kotaları</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="89"/> + <source>Rate limit connections</source> + <translation>Bağlantı hızlarını sınırla</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="373"/> + <source>Your protobuf version is incompatible, you need to install protobuf 3.8.0 or superior +(pip3 install --ignore-installed protobuf==3.8.0)</source> + <translation>protobuf sürümünüz uyumsuz, protobuf 3.8.0 veya üstünü kurmanız gerekiyor +(pip3 install --ignore-installed protobuf==3.8.0)</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="397"/> + <source>Rule deleted</source> + <translation>Kural silindi</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="401"/> + <source>Rule added</source> + <translation>Kural eklendi</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="420"/> + <source>You can use ',' or '-' to specify multiple ports/IPs or ranges/values:<br><br>ports: 22 or 22,443 or 50000-60000<br>IPs: 192.168.1.1 or 192.168.1.30-192.168.1.130<br>Values: echo-reply,echo-request<br>Values: new,established,related</source> + <translation>Birden fazla bağlantı noktası/IP veya aralık/değer belirtmek için ',' veya '-' kullanabilirsiniz:<br><br>bağlantı noktaları: 22 veya 22,443 veya 50000-60000<br>IP adresleri: 192.168.1.1 veya 192.168.1.30-192.168 .1.130<br>Değerler: echo-reply,echo-request<br>Değerler: new,established,related</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="440"/> + <source>Deleting rule, wait</source> + <translation>Kural siliniyor, bekleyin</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="443"/> + <source>Error updating rule</source> + <translation>Kural güncellenirken hata oluştu</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="483"/> + <source>Adding rule, wait</source> + <translation>Kural ekleniyor, bekleyin</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="492"/> + <source><select a statement></source> + <translation><bir ifade seçin></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="787"/> + <source>Equal</source> + <translation>Eşit</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="788"/> + <source>Not equal</source> + <translation>Eşit değil</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="789"/> + <source>Greater or equal than</source> + <translation>Büyük veya eşit</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="790"/> + <source>Greater than</source> + <translation>Büyük</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="791"/> + <source>Less or equal than</source> + <translation>Küçük veya eşit</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="792"/> + <source>Less than</source> + <translation>Küçük</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1350"/> + <source>Firewall rule</source> + <translation>Güvenlik duvarı kuralı</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="885"/> + <source>Simple</source> + <translation>Basit</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="890"/> + <source>Advanced</source> + <translation>Gelişmiş</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1046"/> + <source>This rule is not supported yet.</source> + <translation>Bu kural henüz desteklenmiyor.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1111"/> + <source>Exclude service</source> + <translation>Hizmeti hariç tut</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1123"/> + <source>Allow inbound connections to the selected port.</source> + <translation>Seçilen bağlantı noktasına gelen bağlantılara izin ver.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1125"/> + <source>Allow outbound connections to the selected port.</source> + <translation>Seçilen bağlantı noktasına giden bağlantılara izin ver.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1201"/> + <source>select a statement.</source> + <translation>bir ifade seçin.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1217"/> + <source>value cannot be 0 or empty.</source> + <translation>değer 0 veya boş olamaz.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1229"/> + <source>the value format is 1024/kbytes (or bytes, mbytes, gbytes)</source> + <translation>değer biçimi 1024/kbayttır (veya bayt, mbayt, gbayt)</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1240"/> + <source>the value format is 1024/kbytes/second (or bytes, mbytes, gbytes)</source> + <translation>değer biçimi 1024/kbayt/saniyedir (veya bayt, mbayt, gbayt)</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1243"/> + <source>rate-limit not valid, use: bytes, kbytes, mbytes or gbytes.</source> + <translation>hız sınırı geçerli değil, şunları kullanın: bayt, kbayt, mbayt veya gbayt.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1245"/> + <source>time-limit not valid, use: second, minute, hour or day</source> + <translation>zaman sınırı geçerli değil, şunları kullanın: saniye, dakika, saat veya gün</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1293"/> + <source>port not valid.</source> + <translation>bağlantı noktası geçerli değil.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="108"/> + <source> +Supported formats: + + - Simple: 23 + - Ranges: 80-1024 + - Multiple ports: 80,443,8080 +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="134"/> + <source> +Supported formats: + + - Simple: 1.2.3.4 + - IP ranges: 1.2.3.100-1.2.3.200 + - Network ranges: 1.2.3.4/24 +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="147"/> + <source>Match input interface. Regular expressions not allowed.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="154"/> + <source>Match output interface. Regular expressions not allowed.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="161"/> + <source>Set a conntrack mark on the connection, in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="171"/> + <source>Match a conntrack mark of the connection, in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="178"/> + <source>Match conntrack states. + +Supported formats: + - Simple: new + - Multiple states separated by commas: related,new +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="193"/> + <source> +Match packet's metainformation. + +Value must be in decimal format, except for the "l4proto" option. +For l4proto it can be a lower case string, for example: + tcp + udp + icmp, + etc + +If the value is decimal for protocol or lproto, it'll use it as the code of +that protocol. +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="213"/> + <source>Set a mark on the packet matching the specified conditions. The value is in decimal format.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="221"/> + <source> +Match ICMP codes. + +Supported formats: + - Simple: echo-request + - Multiple separated by commas: echo-request,echo-reply +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="234"/> + <source> +Match ICMPv6 codes. + +Supported formats: + - Simple: echo-request + - Multiple separated by commas: echo-request,echo-reply +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="247"/> + <source>Print a message when this rule matches a packet.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="254"/> + <source> +Apply quotas on connections. + +For example when: + - "quota over 10/mbytes" -> apply the Action defined (DROP) + - "quota until 10/mbytes" -> apply the Action defined (ACCEPT) + +The value must be in the format: VALUE/UNITS, for example: + - 10mbytes, 1/gbytes, etc +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="286"/> + <source> +Apply limits on connections. + +For example when: + - "limit over 10/mbytes/minute" -> apply the Action defined (DROP, ACCEPT, etc) + (When there're more than 10MB per minute, apply an Action) + + - "limit until 10/mbytes/hour" -> apply the Action defined (ACCEPT) + +The value must be in the format: VALUE/UNITS/TIME, for example: + - 10/mbytes/minute, 1/gbytes/hour, etc +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="607"/> + <source>num</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="621"/> + <source>to</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>menu_close</name> + <message> + <location filename="../../../opensnitch/service.py" line="131"/> + <source>Close</source> + <translation type="obsolete">Cerrar</translation> + </message> +</context> +<context> + <name>menu_help</name> + <message> + <location filename="../../../opensnitch/service.py" line="126"/> + <source>Help</source> + <translation type="obsolete">Ayuda</translation> + </message> +</context> +<context> + <name>menu_statistics</name> + <message> + <location filename="../../../opensnitch/service.py" line="120"/> + <source>Statistics</source> + <translation type="obsolete">Eventos</translation> + </message> +</context> +<context> + <name>messages</name> + <message> + <location filename="../../../opensnitch/service.py" line="281"/> + <source>Info</source> + <translation>Bilgi</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="285"/> + <source>Error</source> + <translation>Hata</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="289"/> + <source>Warning</source> + <translation>Uyarı</translation> + </message> +</context> +<context> + <name>notifications</name> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="654"/> + <source>System notifications are not available, you need to install python3-notify2.</source> + <translation>Sistem bildirimleri kullanılamıyor, python3-notify2 kurmanız gerekiyor.</translation> + </message> +</context> +<context> + <name>popups</name> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="115"/> + <source>Allow</source> + <translation>İzin ver</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="114"/> + <source>Deny</source> + <translation>Reddet</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/> + <source>forever</source> + <translation>sonsuza kadar</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="331"/> + <source>Outgoing connection</source> + <translation>Giden bağlantı</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="336"/> + <source>Process launched from:</source> + <translation>İşlem şuradan başlatıldı:</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="377"/> + <source>from this command line</source> + <translation>bu komut satırından</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="373"/> + <source>from this executable</source> + <translation>bu programdan</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="208"/> + <source>Unknown process</source> + <translation type="obsolete">Proceso no encontrado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="50"/> + <source>until reboot</source> + <translation>yeniden başlatılana kadar</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="379"/> + <source>to port {0}</source> + <translation>{0} bağlantı noktasına</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="222"/> + <source><b>%s</b> is connecting to <b>%s</b> on %s port %d</source> + <translation type="obsolete"><b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="228"/> + <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source> + <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="442"/> + <source>to {0}</source> + <translation>{0} hedefine</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="382"/> + <source>from user {0}</source> + <translation>{0} kullanıcısından</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="399"/> + <source>to {0}.*</source> + <translation>{0}.* hedefine</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="452"/> + <source>to *.{0}</source> + <translation>*.{0} hedefine</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="378"/> + <source>to *{0}</source> + <translation type="obsolete">*{0} hedefine</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="486"/> + <source><b>Remote</b> process %s running on <b>%s</b></source> + <translation>%s <b>uzak</b> işlemi, <b>%s</b> üzerinde çalışıyor</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="490"/> + <source>is connecting to <b>%s</b> on %s port %d</source> + <translation><b>%s</b> hedefine bağlanıyor, %s bağlantı noktası %s</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="502"/> + <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source> + <translation><b>%s</b> çözümlemeye çalışıyor, %s aracılığıyla, %s bağlantı noktası %d</translation> + </message> + <message> + <location filename="../../../opensnitch/notifications.py" line="122"/> + <source>New outgoing connection</source> + <translation>Yeni giden bağlantı</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="386"/> + <source>from this PID</source> + <translation>bu işlem kimliğinden</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="116"/> + <source>Reject</source> + <translation>Geri çevir</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="497"/> + <source>is connecting to <b>%s</b>, %s</source> + <translation><b>%s</b> hedefine bağlanıyor, %s</translation> + </message> + <message> + <location filename="../../../opensnitch/notifications.py" line="42"/> + <source>Open</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>popups2</name> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="254"/> + <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source> + <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation> + </message> +</context> +<context> + <name>preferences</name> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="171"/> + <source>Exception saving config: %s</source> + <translation type="obsolete">Error al guarda la configuración: %s</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="177"/> + <source>Applying configuration on %s ...</source> + <translation type="obsolete">Aplicando configuración en %s ...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="292"/> + <source>Server address can not be empty</source> + <translation>Sunucu adresi boş olamaz</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="227"/> + <source>Error loading %s configuration</source> + <translation type="obsolete">Error al cargar la configuración %s</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="568"/> + <source>Configuration applied.</source> + <translation>Yapılandırma uygulandı.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="257"/> + <source>Error applying configuration: %s</source> + <translation type="obsolete">Error al aplicar la configuración: %s</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="386"/> + <source>Exception saving config: {0}</source> + <translation>Yapılandırma kaydedilirken istisna oluştu: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="500"/> + <source>Applying configuration on {0} ...</source> + <translation>{0} üzerinde yapılandırma uygulanıyor...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="321"/> + <source>Error loading {0} configuration</source> + <translation>{0} yapılandırması yüklenirken hata oluştu</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="570"/> + <source>Error applying configuration: {0}</source> + <translation>Yapılandırma uygulanırken hata oluştu: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> + <source>Warning</source> + <translation>Uyarı</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="410"/> + <source>You must select a file for the database<br>or choose "In memory" type.</source> + <translation>Veri tabanı için bir dosya seçmelisiniz<br>veya "Bellekte" türünü seçmelisiniz.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="401"/> + <source>DB type changed</source> + <translation>V.T. türü değişti</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="38"/> + <source>Restart the GUI in order effects to take effect</source> + <translation>Değişikliklerin etkili olabilmesi için grafiksel arayüzü yeniden başlatın</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="607"/> + <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source> + <translation>Yardımı görüntülemek için fareyi metinlerin üzerine getirin<br><br>Wiki sayfasını ziyaret etmeyi unutmayın: <a href="{0}">{0}</a></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="466"/> + <source>System</source> + <translation>Sistem</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="187"/> + <source>Themes not available. Install qt-material: pip3 install qt-material</source> + <translation>Temalar kullanılamıyor. qt-material kurun: pip3 install qt-material</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> + <source>UI theme changed</source> + <translation>Kullanıcı arayüzü teması değiştirildi</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="467"/> + <source>Restart the GUI in order to apply the new theme</source> + <translation>Yeni temayı uygulamak için grafiksel arayüzü yeniden başlatın</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="388"/> + <source>There're no nodes connected</source> + <translation>Bağlı düğüm yok</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="508"/> + <source>Ok</source> + <translation>Tamam</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="472"/> + <source>Restart the GUI in order changes to take effect</source> + <translation type="obsolete">Değişikliklerin etkili olması için grafiksel arayüzü yeniden başlatın</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="520"/> + <source>Exception saving node config {0}: {1}</source> + <translation>{0} düğüm yapılandırması kaydedilirken hata oluştu: {1}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="164"/> + <source>System default</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="433"/> + <source>Language changed</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>proc_details</name> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="100"/> + <source><b>Error loading process information:</b> <br><br> + +</source> + <translation><b>İşlem bilgileri yüklenirken hata oluştu:</b> <br><br> + +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="119"/> + <source><b>Error stopping monitoring process:</b><br><br></source> + <translation><b>İşlemin izlenmesi durdurulurken hata oluştu:</b><br><br></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="159"/> + <source>loading...</source> + <translation>yükleniyor...</translation> + </message> +</context> +<context> + <name>rules</name> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="228"/> + <source>There're no nodes connected.</source> + <translation>Bağlı düğüm yok.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="271"/> + <source>Rule applied.</source> + <translation>Kural uygulandı.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="123"/> + <source>Error applying rule: %s</source> + <translation type="obsolete">Error al aplicar la regla: %s</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="641"/> + <source>protocol can not be empty, or uncheck it</source> + <translation>iletişim kuralı boş olamaz veya işaretini kaldırın</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="655"/> + <source>Protocol regexp error</source> + <translation>İletişim kuralı düzenli ifade hatası</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="659"/> + <source>process path can not be empty</source> + <translation>işlem yolu boş olamaz</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="673"/> + <source>Process path regexp error</source> + <translation>İşlem yolu düzenli ifade hatası</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="677"/> + <source>command line can not be empty</source> + <translation>komut satırı boş olamaz</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="691"/> + <source>Command line regexp error</source> + <translation>Komut satırı düzenli ifade hatası</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="731"/> + <source>Dest port can not be empty</source> + <translation>Hedef bağlantı noktası boş olamaz</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="745"/> + <source>Dst port regexp error</source> + <translation>Hedef bağlantı noktası düzenli ifade hatası</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="749"/> + <source>Dest host can not be empty</source> + <translation>Hedef ana makine boş olamaz</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="763"/> + <source>Dst host regexp error</source> + <translation>Hedef ana makine düzenli ifade hatası</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="805"/> + <source>Dest IP/Network can not be empty</source> + <translation>Hedef IP/Ağ boş olamaz</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="831"/> + <source>Dst IP regexp error</source> + <translation>Hedef IP düzenli ifade hatası</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="843"/> + <source>User ID can not be empty</source> + <translation>Kullanıcı kimliği boş olamaz</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="857"/> + <source>User ID regexp error</source> + <translation>Kullanıcı kimliği düzenli ifade hatası</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="273"/> + <source>Error applying rule: {0}</source> + <translation>Kural uygulanırken hata oluştu: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="931"/> + <source>Lists field cannot be empty</source> + <translation>Listeler alanı boş olamaz</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="933"/> + <source>Lists field must be a directory</source> + <translation>Listeler alanı bir dizin olmalıdır</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="976"/> + <source><b>Rule not supported</b></source> + <translation><b>Kural desteklenmiyor</b></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="539"/> + <source><b>Error loading rule</b></source> + <translation><b>Kural yüklenirken hata oluştu</b></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="245"/> + <source>There's already a rule with this name.</source> + <translation>Bu ada sahip bir kural zaten var.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="861"/> + <source>PID field can not be empty</source> + <translation>İşlem kimliği alanı boş olamaz</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="875"/> + <source>PID field regexp error</source> + <translation>İşlem kimliği alanı düzenli ifade hatası</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="963"/> + <source>Select at least one field.</source> + <translation>En az bir alan seçin.</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="695"/> + <source>Network interface can not be empty</source> + <translation>Ağ arayüzü boş olamaz</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="709"/> + <source>Network interface regexp error</source> + <translation>Ağ arayüzü düzenli ifade hatası</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="713"/> + <source>Source port can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="727"/> + <source>Source port regexp error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="767"/> + <source>Source IP/Network can not be empty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="793"/> + <source>Source IP regexp error</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>stats</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="313"/> + <source>Not running</source> + <translation>Çalışmıyor</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="314"/> + <source>Disabled</source> + <translation>Devre dışı</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="315"/> + <source>Running</source> + <translation>Çalışıyor</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="412"/> + <source>OpenSnitch Network Statistics</source> + <translation type="obsolete">Eventos de OpenSnitch</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="414"/> + <source>OpenSnitch Network Statistics for</source> + <translation type="obsolete">Eventos de OpenSnitch de</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1189"/> + <source> Your are about to delete this rule. </source> + <translation> Bu kuralı silmek üzeresiniz. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> + <source> Are you sure?</source> + <translation> Emin misiniz?</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="636"/> + <source>OpenSnitch Network Statistics {0}</source> + <translation>OpenSnitch Ağ İstatistikleri {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="638"/> + <source>OpenSnitch Network Statistics for {0}</source> + <translation>{0} için OpenSnitch Ağ İstatistikleri</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> + <source>Name</source> + <translation type="obsolete">Nombre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Address</source> + <translation type="obsolete">Dirección</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> + <source>Status</source> + <translation type="obsolete">Estado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> + <source>Hostname</source> + <translation type="obsolete">Hostname</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> + <source>Version</source> + <translation type="obsolete">Versión</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="834"/> + <source>Rules</source> + <translation type="unfinished">Reglas</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Time</source> + <translation type="obsolete">Hora</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="875"/> + <source>Action</source> + <translation type="unfinished">Acción</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> + <source>Duration</source> + <translation type="obsolete">Duración</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> + <source>Node</source> + <translation type="obsolete">Nodo</translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="18"/> + <source>Hits</source> + <translation>Kullanıldı</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>Protocol</source> + <translation type="obsolete">Protocolo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2581"/> + <source>Save as CSV</source> + <translation>CSV olarak kaydet</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Enabled</source> + <translation type="obsolete">Habilitado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="961"/> + <source>Delete</source> + <translation>Sil</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="948"/> + <source>always</source> + <translation type="obsolete">siempre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="580"/> + <source><b>Error:</b><br><br>{0}</source> + <translation type="obsolete"><b>Error:</b><br><br>{0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="954"/> + <source>Disable</source> + <translation>Devre dışı bırak</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="956"/> + <source>Enable</source> + <translation>Etkinleştir</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="959"/> + <source>Duplicate</source> + <translation>Çoğalt</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="960"/> + <source>Edit</source> + <translation>Düzenle</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1248"/> + <source>Rule not found by that name and node</source> + <translation>Bu ada ve düğüme göre kural bulunamadı</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1301"/> + <source><b>Error:</b><br><br></source> + <comment>{0}</comment> + <translation><b>Hata:</b><br><br></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1308"/> + <source>Warning:</source> + <translation>Uyarı:</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="940"/> + <source>Allow</source> + <translation>İzin ver</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="941"/> + <source>Deny</source> + <translation>Reddet</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="945"/> + <source>Always</source> + <translation>Her zaman</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="946"/> + <source>Until reboot</source> + <translation>Yeniden başlatılana kadar</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1711"/> + <source> You are about to delete this rule. </source> + <translation> Bu kuralı silmek üzeresiniz. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> + <source>Process</source> + <translation type="obsolete">Aplicación</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> + <source>Destination</source> + <translation type="obsolete">Destino</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> + <source>Rule</source> + <translation type="obsolete">Regla</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> + <source>UserID</source> + <translation type="obsolete">UserID</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> + <source>LastConnection</source> + <translation type="obsolete">Última Conexión</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> + <source>Name</source> + <comment>xxxxx</comment> + <translation type="obsolete">Nombre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> + <source>Name</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Nombre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Address</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Dirección</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> + <source>Status</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Estado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> + <source>Hostname</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Hostname</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> + <source>Version</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Versión</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> + <source>Rules</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Reglas</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Time</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Hora</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> + <source>Action</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Acción</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> + <source>Duration</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Duración</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> + <source>Node</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Nodo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Enabled</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Habilitado</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> + <source>Hits</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Total</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>Protocol</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Protocolo</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> + <source>Process</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Aplicación</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> + <source>Destination</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Destino</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> + <source>Rule</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">Regla</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> + <source>UserID</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">UserID</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> + <source>LastConnection</source> + <comment>This is a word, without spaces</comment> + <translation type="obsolete">ÚltimaConexión</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="287"/> + <source>Name</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Ad</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="288"/> + <source>Address</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Adres</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="289"/> + <source>Status</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Durum</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="290"/> + <source>Hostname</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Ana makine adı</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="423"/> + <source>Version</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Sürüm</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="420"/> + <source>Rules</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Kurallar</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Time</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Zaman</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> + <source>Action</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Eylem</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> + <source>Duration</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Süre</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> + <source>Node</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Düğüm</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Enabled</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Etkin</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="438"/> + <source>Hits</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Kullanıldı</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> + <source>Protocol</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>İletişim kuralı</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Process</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>İşlem</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>Destination</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Hedef</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> + <source>Rule</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Kural</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> + <source>UserID</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>KullanıcıKimliği</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="311"/> + <source>LastConnection</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>SonBağlantı</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> + <source>Args</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation type="obsolete">Argümanlar</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> + <source>DstIP</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>HedefIP</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> + <source>DstHost</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>HedefAnaMakine</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> + <source>DstPort</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>HedefBağlantıNoktası</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="776"/> + <source>New node connected</source> + <translation>Yeni düğüm bağlandı</translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="17"/> + <source>What</source> + <translation>Ne</translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="19"/> + <source>Network name</source> + <translation>Ağ adı</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="291"/> + <source>Uptime</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Çalışma süresi</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="300"/> + <source>Precedence</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Öncelik</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="421"/> + <source>Connections</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Bağlantılar</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="422"/> + <source>Dropped</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Bırakıldı</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="437"/> + <source>What</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Ne</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="931"/> + <source>Apply to</source> + <translation>Uygula</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="942"/> + <source>Reject</source> + <translation>Geri çevir</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> + <source>Description</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Açıklama</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> + <source>Cmdline</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>Komut satırı</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="406"/> + <source>Export rules</source> + <translation>Kuralları dışa aktar</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="407"/> + <source>Import rules</source> + <translation>Kuralları içe aktar</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="408"/> + <source>Export events to CSV</source> + <translation>Olayları CSV dosyasına aktar</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> + <source>Quit</source> + <translation>Çıkış</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="932"/> + <source>Export</source> + <translation>Dışa aktar</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="964"/> + <source>To clipboard</source> + <translation>Panoya</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="965"/> + <source>To disk</source> + <translation>Diske</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2523"/> + <source>Select a directory to export rules</source> + <translation>Kuralları dışa aktarmak için bir dizin seçin</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1191"/> + <source> Your are about to delete this entry. </source> + <translation> Bu girdiyi silmek üzeresiniz. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1678"/> + <source> You are about to delete this node. </source> + <translation> Bu düğümü silmek üzeresiniz. </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1687"/> + <source><b>Error deleting node</b><br><br></source> + <comment>{0}</comment> + <translation><b>Düğüm silinirken hata oluştu</b><br><br></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2478"/> + <source>Error exporting rules</source> + <translation>Kuralları dışa aktarırken hata oluştu</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2552"/> + <source>Select a directory with rules to import (JSON files)</source> + <translation>İçe aktarılacak kuralları (JSON dosyaları) içeren bir dizin seçin</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2566"/> + <source>Rules imported fine</source> + <translation>Kurallar başarıyla içe aktarıldı</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="211"/> + <source>WARNING</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="833"/> + <source>Details</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="835"/> + <source>New</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>stats_deleterule</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="774"/> + <source> Your are about to delete this rule. </source> + <translation type="obsolete"> Estás a punto de borrar esta regla. </translation> + </message> +</context> +<context> + <name>stats_deleterule2</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="776"/> + <source> Are you sure?</source> + <translation type="obsolete"> ¿Estás seguro?</translation> + </message> +</context> +<context> + <name>stats_disabled</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="74"/> + <source>Disabled</source> + <translation type="obsolete">Deshabilitado</translation> + </message> +</context> +<context> + <name>stats_notrunning</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="73"/> + <source>Not running</source> + <translation type="obsolete">Parado</translation> + </message> +</context> +<context> + <name>stats_running</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="75"/> + <source>Running</source> + <translation type="obsolete">Interceptando</translation> + </message> +</context> +<context> + <name>stats_wintitle</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="409"/> + <source>OpenSnitch Network Statistics</source> + <translation type="obsolete">Eventos de red OpenSnitch</translation> + </message> +</context> +<context> + <name>stats_wintitle2</name> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="411"/> + <source>OpenSnitch Network Statistics for</source> + <translation type="obsolete">Eventos de OpenSnitch de</translation> + </message> +</context> +</TS> diff --git a/ui/i18n/locales/zh_TW/opensnitch-zh_TW.ts b/ui/i18n/locales/zh_TW/opensnitch-zh_TW.ts new file mode 100644 index 0000000..4c4582a --- /dev/null +++ b/ui/i18n/locales/zh_TW/opensnitch-zh_TW.ts @@ -0,0 +1,3043 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1"> +<context> + <name>Dialog</name> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="34"/> + <source>opensnitch-qt</source> + <translation>opensnitch-qt</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="300"/> + <source>User ID</source> + <translation>使用者 ID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="334"/> + <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source> + <translation><html><head/><body><p><span style=" font-weight:600;">執行來自</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="647"/> + <source>TextLabel</source> + <translation>文字標籤</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="437"/> + <source>Source IP</source> + <translation>來源 IP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="458"/> + <source>Process ID</source> + <translation>處理程序 ID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="601"/> + <source>Destination IP</source> + <translation>目標 IP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="622"/> + <source>Dst Port</source> + <translation>目標連接埠</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="702"/> + <source>from this executable</source> + <translation>來自此執行檔</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="707"/> + <source>from this command line</source> + <translation>來自此命令列</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="712"/> + <source>this destination port</source> + <translation>此目標連接埠</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="717"/> + <source>this user</source> + <translation>此使用者</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="722"/> + <source>this destination ip</source> + <translation>此目標 IP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="727"/> + <source>from this PID</source> + <translation>來自此 PID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="751"/> + <source>once</source> + <translation>一次</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="756"/> + <source>30s</source> + <translation>30 秒</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="761"/> + <source>5m</source> + <translation>5 分鐘</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="766"/> + <source>15m</source> + <translation>15 分鐘</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="771"/> + <source>30m</source> + <translation>30 分鐘</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="776"/> + <source>1h</source> + <translation>1 小時</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="781"/> + <source>until reboot</source> + <translation>持續到重新啟動</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="786"/> + <source>forever</source> + <translation>永久</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="809"/> + <source>action</source> + <translation>動作</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="337"/> + <source>Allow</source> + <translation>允許</translation> + </message> + <message> + <location filename="../../../opensnitch/res/prompt.ui" line="865"/> + <source>+</source> + <translation>+</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="14"/> + <source>Firewall</source> + <translation>防火牆</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="55"/> + <source><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></source> + <translation><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">防火牆</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="275"/> + <source>Profile</source> + <translation>設定檔</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="346"/> + <source>Deny</source> + <translation>拒絕</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="313"/> + <source>Outbound</source> + <translation>對外</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="320"/> + <source>Inbound</source> + <translation>對內</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="375"/> + <source>Allow inbound connections to a port</source> + <translation>允許一個連接埠的對內連線</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="378"/> + <source>Allow service (IN)</source> + <translation>允許服務 (對內)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="398"/> + <source>Exclude outbound connections to a port from being intercepted</source> + <translation>排除被攔截到一個連接埠的對外連線</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="407"/> + <source>Allow service (OUT)</source> + <translation>允許服務 (對外)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall.ui" line="427"/> + <source>New rule</source> + <translation>新增規則</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="421"/> + <source>Close</source> + <translation>關閉</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="14"/> + <source>Firewall rule</source> + <translation>防火牆規則</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="26"/> + <source>Node</source> + <translation>節點</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="38"/> + <source>Enable</source> + <translation>啟用</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="50"/> + <source>Description</source> + <translation>描述</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="90"/> + <source>Simple</source> + <translation>簡易</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="154"/> + <source>Add new condition</source> + <translation>新增條件</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="177"/> + <source>Remove selected condition</source> + <translation>移除選定的條件</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="221"/> + <source>Direction</source> + <translation>方向</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="232"/> + <source>IN</source> + <translation>輸入</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="241"/> + <source>OUT</source> + <translation>輸出</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="250"/> + <source>FORWARD</source> + <translation>轉發</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="255"/> + <source>PREROUTING</source> + <translation>預路由</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="260"/> + <source>POSTROUTING</source> + <translation>後路由</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="268"/> + <source>Action</source> + <translation>動作</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="279"/> + <source>ACCEPT</source> + <translation>接受</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="288"/> + <source>DROP</source> + <translation>丟棄</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="297"/> + <source>REJECT</source> + <translation>拒絕</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="306"/> + <source>RETURN</source> + <translation>返回</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="315"/> + <source>QUEUE</source> + <translation>佇列</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="323"/> + <source>DNAT</source> + <translation>DNAT</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="328"/> + <source>SNAT</source> + <translation>SNAT</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="333"/> + <source>REDIRECT</source> + <translation>重新導向</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="349"/> + <source>depending on the Action (i.e.: target), the syntaxis of the parameters will vary. +Some examples: + +QUEUE -> num 0 (or 1, 2, ...) +REDIRECT, TPROXY, DNAT, SNAT, MASQUERADE: + to :22 + to 192.168.1.254:8080 + to 192.168.1.254 + to 1024-2048 (masquerade)</source> + <translation>依據動作(例如:目標),參數的語法將有所不同。 +一些範例: + +QUEUE -> num 0(或 1、2、...) +REDIRECT、TPROXY、DNAT、SNAT、MASQUERADE: + to :22 + to 192.168.1.254:8080 + to 192.168.1.254 + to 1024-2048(masquerade)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="432"/> + <source>Clear</source> + <translation>清除</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="443"/> + <source>Delete</source> + <translation>刪除</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="454"/> + <source>Save</source> + <translation>儲存</translation> + </message> + <message> + <location filename="../../../opensnitch/res/firewall_rule.ui" line="465"/> + <source>Add</source> + <translation>新增</translation> + </message> +</context> +<context> + <name>PreferencesDialog</name> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="14"/> + <source>Preferences</source> + <translation>偏好設定</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="39"/> + <source>Pop-ups</source> + <translation>彈出視窗</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="64"/> + <source>Default options</source> + <translation>預設選項</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="110"/> + <source>If checked, this field will be selected when a pop-up is displayed</source> + <translation>如果選取,則在顯示彈出視窗時將選擇此欄位</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="81"/> + <source>User ID</source> + <translation>使用者 ID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="97"/> + <source>Destination port</source> + <translation>目標連接埠</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="113"/> + <source>Destination IP</source> + <translation>目標 IP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1173"/> + <source>deny</source> + <translation>拒絕</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1182"/> + <source>allow</source> + <translation>允許</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="147"/> + <source>reject</source> + <translation>拒絕</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="159"/> + <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source> + <translation><html><head/><body><p>彈出視窗的預設動作。</p><p>當新的對外連線即將建立時,此動作將被預設選擇,所以如果逾時,這將是被套用的選項。</p><p><br/></p><p>當彈出視窗正在詢問使用者是否允許或拒絕連線時:</p><p>1. 新的對外連線被拒絕。</p><p>2. 已知的連線依據使用者定義的規則被允許或拒絕。</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="872"/> + <source>Action</source> + <translation>動作</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="179"/> + <source>center</source> + <translation>中央</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="184"/> + <source>top right</source> + <translation>右上</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="189"/> + <source>bottom right</source> + <translation>右下</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="194"/> + <source>top left</source> + <translation>左上</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="199"/> + <source>bottom left</source> + <translation>左下</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1205"/> + <source>once</source> + <translation>一次</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="219"/> + <source>30s</source> + <translation>30 秒</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="224"/> + <source>5m</source> + <translation>5 分鐘</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="229"/> + <source>15m</source> + <translation>15 分鐘</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="234"/> + <source>30m</source> + <translation>30 分鐘</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="239"/> + <source>1h</source> + <translation>1 小時</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="244"/> + <source>until reboot</source> + <translation>持續到重新啟動</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="249"/> + <source>forever</source> + <translation>永久</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="263"/> + <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source> + <translation><html><head/><body><p>預設情況下,當出現新的彈出視窗時,以其最簡單的形式,您將能夠按連線的一個屬性(執行檔、連接埠、IP 等)篩選連線或應用程式。</p><p>使用這些選項,您可以選擇多個欄位來篩選連線。</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="266"/> + <source>Filter connections also by:</source> + <translation>也按以下方式篩選連線:</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="283"/> + <source>by executable</source> + <translation>依執行檔</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="288"/> + <source>by command line</source> + <translation>依命令列</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="293"/> + <source>by destination port</source> + <translation>依目標連接埠</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="298"/> + <source>by destination ip</source> + <translation>依目標 IP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="303"/> + <source>by user id</source> + <translation>依使用者 ID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="308"/> + <source>by PID</source> + <translation>依 PID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="323"/> + <source>Default target</source> + <translation>預設目標</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="330"/> + <source>Default position on screen</source> + <translation>在螢幕上的預設位置</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="340"/> + <source>Pop-up default duration</source> + <translation>彈出視窗的預設持續時間</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="343"/> + <source>Duration</source> + <translation>持續時間</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="356"/> + <source>The advanced view allows you to easily select multiple fields to filter connections</source> + <translation>進階檢視讓您可以輕鬆選擇多個欄位來篩選連線</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="359"/> + <source>Show advanced view by default</source> + <translation>預設顯示進階檢視</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="375"/> + <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source> + <translation><html><head/><body><p>如果選取,彈出視窗將會以進階檢視顯示。</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="466"/> + <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source> + <translation><html><head/><body><p>此逾時是顯示彈出對話框時看到的倒數計時。</p><p>如果未回答彈出視窗,將套用預設選項。</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="469"/> + <source>Default timeout</source> + <translation>預設逾時</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="476"/> + <source>Disable pop-ups, only display a notification</source> + <translation>停用彈出視窗,只顯示通知</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="487"/> + <source>UI</source> + <translation>使用者介面</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1128"/> + <source>General</source> + <translation>一般</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="509"/> + <source>Language</source> + <translation>語言</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="533"/> + <source>System</source> + <translation>系統</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="547"/> + <source>Theme</source> + <translation>主題</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="554"/> + <source>By default the GUI is started when login</source> + <translation>預設登入時啟動圖形介面</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="557"/> + <source>Autostart the GUI upon login</source> + <translation>登入時自動啟動圖形介面</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="568"/> + <source>Server</source> + <translation>伺服器</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="581"/> + <source>4MiB</source> + <translation>4 MiB</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="586"/> + <source>8MiB</source> + <translation>8 MiB</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="591"/> + <source>16MiB</source> + <translation>16 MiB</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="596"/> + <source>32MiB</source> + <translation>32 MiB</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="605"/> + <source>Simple</source> + <translation>簡易</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="610"/> + <source>Simple TLS</source> + <translation>簡易 TLS</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="615"/> + <source>Mutual TLS</source> + <translation>雙向 TLS</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="623"/> + <source>Absolute path to the cert file</source> + <translation>憑證檔案的絕對路徑</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="630"/> + <source>Maximum size of each message from nodes. Default 4MB</source> + <translation>來自節點的每個訊息的最大大小。預設 4MB</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="633"/> + <source>Max gRPC channel size</source> + <translation>gRPC 頻道的最大大小</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="640"/> + <source>Absolute path to the cert key file</source> + <translation>憑證金鑰檔案的絕對路徑</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="647"/> + <source><p>Simple: no authentication, TLS simple/mutual: use SSL certificates to authenticate nodes.</p><p>Visit the wiki for more information.</p></source> + <translation><p>簡易:無驗證,TLS 簡易/雙向:使用 SSL 憑證來驗證節點。</p><p>造訪 wiki 以取得更多資訊。</p></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="650"/> + <source>Authentication type</source> + <translation>驗證類型</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="657"/> + <source>Absolute path to the CA cert file</source> + <translation>CA 憑證檔案的絕對路徑</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="673"/> + <source>Desktop notifications</source> + <translation>桌面通知</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="685"/> + <source>Enable</source> + <translation>啟用</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="703"/> + <source>Use system notifications</source> + <translation>使用系統通知</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="719"/> + <source>Use Qt notifications</source> + <translation>使用 Qt 通知</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="748"/> + <source>Test</source> + <translation>測試</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="769"/> + <source>Events tab columns</source> + <translation>事件標籤欄位</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="808"/> + <source>Time</source> + <translation>時間</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="824"/> + <source>Rule</source> + <translation>規則</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="840"/> + <source>Node</source> + <translation>節點</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="856"/> + <source>Protocol</source> + <translation>通訊協定</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="888"/> + <source>Destination</source> + <translation>目標</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="904"/> + <source>Process</source> + <translation>處理程序</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="914"/> + <source>Command line</source> + <translation>命令列</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="932"/> + <source>Rules</source> + <translation>規則</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="940"/> + <source>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI. + +Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</source> + <translation>選擇此選項時,選定持續時間的規則將不會被新增到 GUI 的臨時規則列表中。 + +臨時規則仍然有效,並且您可以在提示允許/阻擋新連線時使用它們。</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="945"/> + <source>Don't save/Delete rules of duration</source> + <translation>不儲存/刪除持續時間的規則</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="953"/> + <source>any temporary rules</source> + <translation>任何臨時規則</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="963"/> + <source>30s or less</source> + <translation>30 秒或更少</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="968"/> + <source>5m or less</source> + <translation>5 分鐘或更少</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="973"/> + <source>15m or less</source> + <translation>15 分鐘或更少</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="978"/> + <source>30m or less</source> + <translation>30 分鐘或更少</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="983"/> + <source>1h or less</source> + <translation>1 小時或更少</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1007"/> + <source>Nodes</source> + <translation>節點</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1019"/> + <source>HostName</source> + <translation>主機名稱</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1067"/> + <source>Version</source> + <translation>版本</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1109"/> + <source>Apply configuration to all nodes</source> + <translation>將設定套用到所有節點</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1134"/> + <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source> + <translation><html><head/><body><p>節點的位址。</p><p>預設:unix:///tmp/osui.sock(如果是 Unix socket,unix:// 是必須的)</p><p>也可以是帶有連接埠的 IP 位址:127.0.0.1:50051</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1137"/> + <source>Address</source> + <translation>位址</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1148"/> + <source>unix:///tmp/osui.sock</source> + <translation>unix:///tmp/osui.sock</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1156"/> + <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source> + <translation><html><head/><body><p>當沒有連接 UI 時,將進行預設動作。</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1159"/> + <source>Default action when the GUI is disconnected</source> + <translation>GUI 斷開連接時的預設動作</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1194"/> + <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source> + <translation><html><head/><body><p>當沒有連接 UI 時,將進行預設持續時間。</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1197"/> + <source>Default duration</source> + <translation>預設的持續時間</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1210"/> + <source>until restart</source> + <translation>直到重新啟動</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1215"/> + <source>always</source> + <translation>永遠</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1223"/> + <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source> + <translation><html><head/><body><p>如果選取,OpenSnitch 將提示您允許或拒絕沒有相關 PID 的連線,原因有很多,主要是因為連線狀態不佳。</p><p>彈出對話框只會包含有關網路連線的資訊。</p><p>儘管在某些情況下,這些是有效的連線,例如使用 WireGuard 建立 VPN。</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1226"/> + <source>Debug invalid connections</source> + <translation>偵錯無效連線</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1240"/> + <source>Process monitor method</source> + <translation>處理程序監控方法</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1278"/> + <source>Logging</source> + <translation>記錄</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1291"/> + <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source> + <translation><html><head/><body><p>寫入記錄的日誌檔案。<br/></p><p>/dev/stdout 將會將記錄列印到標準輸出。</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1294"/> + <source>Log file</source> + <translation>日誌檔案</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1301"/> + <source><html><head/><body><p>If checked, OpenSnitch will log timestamp microseconds.</p></body></html></source> + <translation><html><head/><body><p>如果選取,OpenSnitch 將記錄時間戳微秒。</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1304"/> + <source>Log timestamp microseconds</source> + <translation>記錄時間戳微秒</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1348"/> + <source><html><head/><body><p>If checked, OpenSnitch will use the UTC timezone for timestamps.</p></body></html></source> + <translation><html><head/><body><p>如果選取,OpenSnitch 將使用 UTC 時區的時間戳。</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1351"/> + <source>Log UTC timestamps</source> + <translation>記錄 UTC 時間戳</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1358"/> + <source>Default log level</source> + <translation>預設記錄等級</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1369"/> + <source>/var/log/opensnitchd.log</source> + <translation>/var/log/opensnitchd.log</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1374"/> + <source>/dev/stdout</source> + <translation>/dev/stdout</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1410"/> + <source>Database</source> + <translation>資料庫</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1445"/> + <source>In memory</source> + <translation>在記憶體中</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1450"/> + <source>File</source> + <translation>檔案</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1464"/> + <source>Database type</source> + <translation>資料庫類型</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1471"/> + <source>Select</source> + <translation>選擇</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1558"/> + <source>minutes</source> + <translation>分鐘</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1590"/> + <source>Minutes between events purges</source> + <translation>事件清除之間的分鐘數</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1616"/> + <source>days</source> + <translation>天</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1629"/> + <source>Maximum days of events to keep</source> + <translation>保留事件的最大天數</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1753"/> + <source>Close</source> + <translation>關閉</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1764"/> + <source>Apply</source> + <translation>套用</translation> + </message> + <message> + <location filename="../../../opensnitch/res/preferences.ui" line="1775"/> + <source>Save</source> + <translation>儲存</translation> + </message> +</context> +<context> + <name>ProcessDetailsDialog</name> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="14"/> + <source>Process details</source> + <translation>處理程序詳細資訊</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="61"/> + <source>loading...</source> + <translation>載入中...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="81"/> + <source>CWD: loading...</source> + <translation>CWD:載入中...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="93"/> + <source>mem stats: loading...</source> + <translation>記憶體狀態:載入中...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="121"/> + <source>Status</source> + <translation>狀態</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="135"/> + <source>Open files</source> + <translation>開啟檔案</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="149"/> + <source>I/O Statistics</source> + <translation>I/O 統計</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="163"/> + <source>Memory mapped files</source> + <translation>記憶體映射檔案</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="177"/> + <source>Stack</source> + <translation>堆疊</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="191"/> + <source>Environment variables</source> + <translation>環境變數</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="210"/> + <source>Application pids</source> + <translation>應用程式 PID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="240"/> + <source>Start or stop monitoring this process</source> + <translation>開始或停止監控此程序</translation> + </message> + <message> + <location filename="../../../opensnitch/res/process_details.ui" line="256"/> + <source>Close</source> + <translation>關閉</translation> + </message> +</context> +<context> + <name>RulesDialog</name> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="20"/> + <source>Rule</source> + <translation>規則</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="56"/> + <source>Action</source> + <translation>動作</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="89"/> + <source>Duration</source> + <translation>持續時間</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="97"/> + <source>once</source> + <translation>一次</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="127"/> + <source>until reboot</source> + <translation>直到重新啟動</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="132"/> + <source>always</source> + <translation>永遠</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="148"/> + <source>Deny will just discard the connection</source> + <translation>阻擋將僅丟棄連線</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="151"/> + <source>Deny</source> + <translation>阻擋</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="165"/> + <source>Reject will drop the connection, and kill the socket that initiated it</source> + <translation>拒絕將會丟棄連線,並終止啟動它的socket</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="168"/> + <source>Reject</source> + <translation>拒絕</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="185"/> + <source>Allow will allow the connection</source> + <translation>允許將允許連線</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="191"/> + <source>Allow</source> + <translation>允許</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="207"/> + <source>Enable</source> + <translation>啟用</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="214"/> + <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one. + +You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example: + +[x] Priority - 000-priority-rule +[ ] Priority - 001-less-priority-rule</source> + <translation>如果選取,此規則將優於其他規則。在此之後不會檢查其他規則。 + +您必須以這樣的方式命名規則,以便首先檢查它,因為它們按字母順序檢查。例如: + +[x] 優先 - 000-priority-rule +[ ] 優先 - 001-less-priority-rule</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="222"/> + <source>Priority rule</source> + <translation>優先規則</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="238"/> + <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them. + +000-allow-localhost +001-deny-broadcast +...</source> + <translation>規則按字母順序檢查,因此您可以相應地命名它們以優先考慮它們。 + +000-allow-localhost +001-deny-broadcast +...</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="245"/> + <source>Name</source> + <translation>名稱</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/> + <source>Node</source> + <translation>節點</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/> + <source>Apply rule to all nodes</source> + <translation>將規則套用到所有節點</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="346"/> + <source>Applications</source> + <translation>應用程式</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="355"/> + <source><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></source> + <translation><html><head/><body><p>此欄位的值始終是執行檔的絕對路徑:/path/to/binary<br/></p><p>範例:</p><p>- 簡易:/path/to/binary</p><p>- 多路徑:^/usr/lib(64|)/firefox/firefox$</p><p>- 多個執行檔:^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$</p><p>- 拒絕/允許從 /tmp 執行:</p><p>^/(var/|)tmp/.*$<br/></p><p>要取得更多範例,請造訪<a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki 頁面</a>或在<a href="https://github.com/evilsocket/opensnitch/discussions">討論論壇</a>提問。</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="365"/> + <source>Is regular expression</source> + <translation>是正規表達式</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="372"/> + <source>From this user ID</source> + <translation>來自此使用者 ID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="379"/> + <source>From this command line</source> + <translation>來自此命令列</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="389"/> + <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source> + <translation><html><head/><body><p>此欄位將包含並符合使用者執行的命令列。<br/></p><p>如果使用者輸入了命令,只會出現該命令:</p><p>telnet 1.2.3.4<br/></p><p>如果使用者輸入了命令的絕對或相對路徑,那就會出現該內容:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="399"/> + <source>From this PID</source> + <translation>來自此 PID</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="466"/> + <source>From this executable</source> + <translation>來自此執行檔</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="473"/> + <source>is regular expression</source> + <translation>是正規表達式</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="485"/> + <source>Network</source> + <translation>網路</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="520"/> + <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source> + <translation><html><head/><body><p>僅允許 TCP、UDP 或 UDPLITE</p><p>您可以使用正規表達式,例如:^(TCP|UDP)$</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="526"/> + <source>TCP</source> + <translation>TCP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="560"/> + <source>ICMP</source> + <translation>ICMP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="565"/> + <source>ICMP6</source> + <translation>ICMP6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="570"/> + <source>SCTP</source> + <translation>SCTP</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="575"/> + <source>SCTP6</source> + <translation>SCTP6</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="586"/> + <source>Commas or spaces are not allowed to specify multiple domains. + +Use regular expressions instead: +.*(opensnitch|duckduckgo).com +.*\.google.com + +or a single domain: +www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... +gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="597"/> + <source>www.domain.org, .*\.domain.org</source> + <translation>www.domain.org, .*\.domain.org</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="604"/> + <source>To this IP / Network</source> + <translation>到此 IP / 網路</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="627"/> + <source>Protocol</source> + <translation>通訊協定</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="754"/> + <source>You can specify a single IP: +- 192.168.1.1 + +or a regular expression: +- 192\.168\.1\.[0-9]+ + +multiple IPs: +- ^(192\.168\.1\.1|172\.16\.0\.1)$ + +You can also specify a subnet: +- 192.168.1.0/24 + +Note: Commas or spaces are not allowed to separate IPs or networks.</source> + <translation>您可以指定單一 IP: +- 192.168.1.1 + +或是正規表達式: +- 192\.168\.1\.[0-9]+ + +多個 IP: +- ^(192\.168\.1\.1|172\.16\.0\.1)$ + +您也可以指定子網: +- 192.168.1.0/24 + +注意:不允許使用逗號或空格分隔 IP 或網路。</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="659"/> + <source>LAN</source> + <translation>區域網路</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="664"/> + <source>MULTICAST</source> + <translation>多播</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="669"/> + <source>127.0.0.0/8</source> + <translation>127.0.0.0/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="674"/> + <source>192.168.0.0/24</source> + <translation>192.168.0.0/24</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="679"/> + <source>192.168.1.0/24</source> + <translation>92.168.1.0/24</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="684"/> + <source>192.168.2.0/24</source> + <translation>192.168.2.0/24</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="689"/> + <source>192.168.0.0/16</source> + <translation>192.168.0.0/16</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="694"/> + <source>169.254.0.0/16</source> + <translation>169.254.0.0/16</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="699"/> + <source>172.16.0.0/12</source> + <translation>172.16.0.0/12</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="704"/> + <source>10.0.0.0/8</source> + <translation>10.0.0.0/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="709"/> + <source>::1/128</source> + <translation>::1/128</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="714"/> + <source>fc00::/7</source> + <translation>fc00::/7</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="719"/> + <source>ff00::/8</source> + <translation>ff00::/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="724"/> + <source>fe80::/10</source> + <translation>fe80::/10</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="729"/> + <source>fd00::/8</source> + <translation>fd00::/8</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="737"/> + <source>From this IP / Network</source> + <translation>來自此 IP / 網路</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="744"/> + <source>To this host</source> + <translation>到此主機</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="857"/> + <source>Network interface</source> + <translation>網路介面</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="866"/> + <source>From this port</source> + <translation>來自此連接埠</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="912"/> + <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source> + <translation><html><head/><body><p>您可以使用正規表達式指定多個連接埠:</p><p>- 53、80 或 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53、443 或 5551、5552、5553 等:</p><p>^(53|443|555[0-9])$</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="896"/> + <source>To this port</source> + <translation>到此連接埠</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="926"/> + <source>List of domains/IPs</source> + <translation>網域/IP 清單</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="932"/> + <source>To this list of network ranges</source> + <translation>到此網路範圍清單</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="939"/> + <source>To this list of IPs</source> + <translation>到此 IP 清單</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="965"/> + <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation><html><head/><body><p>選擇一個含有要封鎖或允許的 IP 清單的檔案的目錄:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>等等。</p><p>每行一個 IP。空行或以 # 開頭的行將被忽略。</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="974"/> + <source>To this list of domains</source> + <translation>到此網域清單</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1000"/> + <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation><html><head/><body><p>選擇一個含有要封鎖或允許的網路範圍清單的檔案的目錄:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>等等。<br/></p><p>每行一個網路範圍。空行或以 # 開頭的行將被忽略。</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1028"/> + <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source> + <translation><html><head/><body><p>選擇一個含有要封鎖或允許的網域清單的檔案的目錄。</p><p>在該目錄中放置含有網域清單的任何副檔名的檔案。</p><p><br/>每個清單條目的格式如下(hosts 格式):</p><p>127.0.0.1 www.domain.com</p><p>或 </p><p>0.0.0.0 www.domain.com</p><p>空行或以 # 開頭的行將被忽略。</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1043"/> + <source>To this list of domains +(regular expressions)</source> + <translation>到此網域清單 +(正規表達式)</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1070"/> + <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source> + <translation><html><head/><body><p>請在目錄中選擇一個含有要封鎖或允許的網域正規表達式的檔案:</p><p>.*\.example\.com</p><p>您也可以直接使用網域:&quot;example.com&quot;,它將符合 whatever.example.com、whatever.example.com.localdomain 等。</p><p>每行輸入一個網域。空行或以 # 開頭的行將被忽略。</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1080"/> + <source>More</source> + <translation>更多</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1086"/> + <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source> + <translation><html><head/><body><p>預設情況下,規則的欄位不區分大小寫,即,如果一個程序試圖存取 gOOgle.CoM,並且您有一個阻擋 .*google.com 的規則,則連線將被阻擋。<br/></p><p>如果您選取此框,則必須指定您要篩選的確切字串(網域、執行檔、命令列)。</p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1089"/> + <source>Case-sensitive</source> + <translation>區分大小寫</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1096"/> + <source>Don't log connections that match this rule</source> + <translation>不記錄符合此規則的連線</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1099"/> + <source>Don't log connections</source> + <translation>不記錄連線</translation> + </message> + <message> + <location filename="../../../opensnitch/res/ruleseditor.ui" line="1145"/> + <source>Description...</source> + <translation>描述...</translation> + </message> +</context> +<context> + <name>StatsDialog</name> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="34"/> + <source>OpenSnitch Network Statistics</source> + <translation>OpenSnitch 網路統計</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="94"/> + <source>Filter</source> + <translation>篩選</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1814"/> + <source>-</source> + <translation>-</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="107"/> + <source>Allow</source> + <translation>允許</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="116"/> + <source>Deny</source> + <translation>阻擋</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="125"/> + <source>Reject</source> + <translation>拒絕</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="143"/> + <source>Ex.: firefox</source> + <translation>例如:firefox</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="180"/> + <source>0</source> + <translation>0</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="205"/> + <source>50</source> + <translation>50</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="210"/> + <source>100</source> + <translation>100</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="215"/> + <source>200</source> + <translation>200</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="220"/> + <source>300</source> + <translation>300</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="233"/> + <source>Delete all intercepted events</source> + <translation>刪除所有被攔截的事件</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="333"/> + <source>Create a new rule</source> + <translation>建立新規則</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="376"/> + <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source> + <translation><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">主機名稱 - 192.168.1.1</span></p></body></html></translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="413"/> + <source>Status</source> + <translation>狀態</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="451"/> + <source>Start or Stop interception</source> + <translation>開始或停止攔截</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="496"/> + <source>Events</source> + <translation>事件</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="829"/> + <source>Nodes</source> + <translation>節點</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="640"/> + <source>Delete this node</source> + <translation>刪除此節點</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="656"/> + <source>Show the preferences of this node</source> + <translation>顯示此節點的偏好設定</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="672"/> + <source>Start or stop interception of this node</source> + <translation>開始或停止攔截此節點</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1721"/> + <source>Rules</source> + <translation>規則</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="780"/> + <source>2</source> + <translation>2</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="785"/> + <source>Application rules</source> + <translation>應用程式規則</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="939"/> + <source>Permanent</source> + <translation>永久</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="948"/> + <source>Temporary</source> + <translation>臨時</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="957"/> + <source>System rules</source> + <translation>系統規則</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="930"/> + <source>All applications</source> + <translation>所有應用程式</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="998"/> + <source>enable</source> + <translation>啟用</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1028"/> + <source>Edit rule</source> + <translation>編輯規則</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1045"/> + <source>Delete rule</source> + <translation>刪除規則</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1069"/> + <source>Hosts</source> + <translation>主機</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1162"/> + <source>Applications</source> + <translation>應用程式</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1278"/> + <source>Addresses</source> + <translation>位址</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1371"/> + <source>Ports</source> + <translation>連接埠</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1458"/> + <source>Users</source> + <translation>使用者</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1565"/> + <source>Connections</source> + <translation>連線</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1617"/> + <source>Dropped</source> + <translation>已丟棄</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1669"/> + <source>Uptime</source> + <translation>運作時間</translation> + </message> + <message> + <location filename="../../../opensnitch/res/stats.ui" line="1788"/> + <source>Version</source> + <translation>版本</translation> + </message> +</context> +<context> + <name>contextual_menu</name> + <message> + <location filename="../../../opensnitch/service.py" line="48"/> + <source>Statistics</source> + <translation>統計</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="49"/> + <source>Enable</source> + <translation>啟用</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="50"/> + <source>Disable</source> + <translation>停用</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="51"/> + <source>Help</source> + <translation>幫助</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="52"/> + <source>Close</source> + <translation>關閉</translation> + </message> +</context> +<context> + <name>firewall</name> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="96"/> + <source>Configuration applied.</source> + <translation>設定已套用。</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="99"/> + <source>Error: {0}</source> + <translation>錯誤: {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="198"/> + <source>Applying changes...</source> + <translation>正在套用更改...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="235"/> + <source>Error getting INPUT chain policy</source> + <translation>取得 INPUT 鏈策略時出錯</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="242"/> + <source>Error getting OUTPUT chain policy</source> + <translation>取得 OUTPUT 鏈策略時出錯</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="295"/> + <source>In order to configure firewall rules from the GUI, we need to use 'nftables' instead of 'iptables'</source> + <translation>為了從 GUI 設定防火牆規則,我們需要使用 'nftables' 而不是 'iptables'</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="309"/> + <source>Enabling firewall...</source> + <translation>正在啟用防火牆...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall.py" line="311"/> + <source>Disabling firewall...</source> + <translation>正在停用防火牆...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="71"/> + <source>Dest Port</source> + <translation>目標連接埠</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="72"/> + <source>Source Port</source> + <translation>來源連接埠</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="73"/> + <source>Dest IP</source> + <translation>目標 IP</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="74"/> + <source>Source IP</source> + <translation>來源 IP</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="75"/> + <source>Input interface</source> + <translation>輸入介面</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="76"/> + <source>Output interface</source> + <translation>輸出介面</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="77"/> + <source>Set conntrack mark</source> + <translation>設定 conntrack 標記</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="78"/> + <source>Match conntrack mark</source> + <translation>符合 conntrack 標記</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="79"/> + <source>Match conntrack state(s)</source> + <translation>符合 conntrack 狀態</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="80"/> + <source>Set mark on packet</source> + <translation>在封包上設定標記</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="81"/> + <source>Match packet information</source> + <translation>符合封包資訊</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="87"/> + <source>Bandwidth quotas</source> + <translation>頻寬配額</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="89"/> + <source>Rate limit connections</source> + <translation>限制連接速率</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="108"/> + <source> +Supported formats: + + - Simple: 23 + - Ranges: 80-1024 + - Multiple ports: 80,443,8080 +</source> + <translation> +支援的格式: + + - 簡易:23 + - 範圍:80-1024 + - 多個連接埠:80,443,8080 +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="134"/> + <source> +Supported formats: + + - Simple: 1.2.3.4 + - IP ranges: 1.2.3.100-1.2.3.200 + - Network ranges: 1.2.3.4/24 +</source> + <translation> +支援的格式: + + - 簡易:1.2.3.4 + - IP 範圍:1.2.3.100-1.2.3.200 + - 網路範圍:1.2.3.4/24 +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="147"/> + <source>Match input interface. Regular expressions not allowed. +Use * to match multiple interfaces.</source> + <translation>符合輸入介面。不允許使用正規表達式。 +使用 * 符號符合多個介面。</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="155"/> + <source>Match output interface. Regular expressions not allowed. +Use * to match multiple interfaces.</source> + <translation>符合輸出介面。不允許使用正規表達式。 +使用 * 符號符合多個介面。</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="163"/> + <source>Set a conntrack mark on the connection, in decimal format.</source> + <translation>在連線上設定 conntrack 標記,以十進位格式。</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="173"/> + <source>Match a conntrack mark of the connection, in decimal format.</source> + <translation>符合連線的 conntrack 標記,以十進位格式。</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="180"/> + <source>Match conntrack states. + +Supported formats: + - Simple: new + - Multiple states separated by commas: related,new +</source> + <translation>符合 conntrack 狀態。 + +支援的格式: + - 簡易:new + - 逗號分隔狀態:related,new +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="195"/> + <source> +Match packet's metainformation. + +Value must be in decimal format, except for the "l4proto" option. +For l4proto it can be a lower case string, for example: + tcp + udp + icmp, + etc + +If the value is decimal for protocol or lproto, it'll use it as the code of +that protocol. +</source> + <translation> +符合封包的附加資訊。 + +值必須使用十進位格式,唯獨 "l4proto" 選項除外。 +對於 l4proto,它可以是小寫字串,例如: + tcp + udp + icmp + 等等 + +如果協議或 lproto 的值為十進位,它將用作該協議的代碼。 +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="215"/> + <source>Set a mark on the packet matching the specified conditions. The value is in decimal format.</source> + <translation>在符合指定條件的封包上設定標記。值為十進位格式。</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="223"/> + <source> +Match ICMP codes. + +Supported formats: + - Simple: echo-request + - Multiple separated by commas: echo-request,echo-reply +</source> + <translation> +符合 ICMP 代碼。 + +支援的格式: + - 簡易:echo-request + - 逗號分隔:echo-request,echo-reply +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="236"/> + <source> +Match ICMPv6 codes. + +Supported formats: + - Simple: echo-request + - Multiple separated by commas: echo-request,echo-reply +</source> + <translation> +符合 ICMPv6 代碼。 + +支援的格式: + - 簡易:echo-request + - 逗號分隔:echo-request,echo-reply +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="249"/> + <source>Print a message when this rule matches a packet.</source> + <translation>當此規則符合封包時,列印一條訊息。</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="256"/> + <source> +Apply quotas on connections. + +For example when: + - "quota over 10/mbytes" -> apply the Action defined (DROP) + - "quota until 10/mbytes" -> apply the Action defined (ACCEPT) + +The value must be in the format: VALUE/UNITS, for example: + - 10mbytes, 1/gbytes, etc +</source> + <translation> +對連線套用配額。 + +例如: + - "quota over 10/mbytes" -> 套用定義的動作 (DROP) + - "quota until 10/mbytes" -> 套用定義的動作 (ACCEPT) + +值必須為 VALUE/UNITS 格式,例如: + - 10mbytes, 1/gbytes, 等等 +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="288"/> + <source> +Apply limits on connections. + +For example when: + - "limit over 10/mbytes/minute" -> apply the Action defined (DROP, ACCEPT, etc) + (When there're more than 10MB per minute, apply an Action) + + - "limit until 10/mbytes/hour" -> apply the Action defined (ACCEPT) + +The value must be in the format: VALUE/UNITS/TIME, for example: + - 10/mbytes/minute, 1/gbytes/hour, etc +</source> + <translation> +對連線套用限制。 + +例如: + - "limit over 10/mbytes/minute" -> 套用定義的動作 (DROP, ACCEPT, 等等) + (當每分鐘超過 10MB 時,套用一個動作) + + - "limit until 10/mbytes/hour" -> 套用定義的動作 (ACCEPT) + +值必須為 VALUE/UNITS/TIME 格式,例如: + - 10/mbytes/minute, 1/gbytes/hour, 等等 +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="376"/> + <source>Your protobuf version is incompatible, you need to install protobuf 3.8.0 or superior +(pip3 install --ignore-installed protobuf==3.8.0)</source> + <translation>您的 protobuf 版本不相容,您需要安裝 protobuf 3.8.0 或更高版本 +(pip3 install --ignore-installed protobuf==3.8.0)</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="405"/> + <source>Rule deleted</source> + <translation>規則已刪除</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="411"/> + <source>Rule saved</source> + <translation>規則已儲存</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="413"/> + <source>Rule added</source> + <translation>規則已新增</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="423"/> + <source>Error saving rule</source> + <translation>儲存規則時出錯</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="450"/> + <source>You can use ',' or '-' to specify multiple ports/IPs or ranges/values:<br><br>ports: 22 or 22,443 or 50000-60000<br>IPs: 192.168.1.1 or 192.168.1.30-192.168.1.130<br>Values: echo-reply,echo-request<br>Values: new,established,related</source> + <translation>您可以使用 ',' 或 '-' 來指定多個連接埠/IP 或範圍/值:<br><br>連接埠:22 或 22,443 或 50000-60000<br>IP:192.168.1.1 或 192.168.1.30-192.168.1.130<br>值:echo-reply,echo-request<br>值:new,established,related</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="470"/> + <source>Deleting rule, wait</source> + <translation>正在刪除規則,請稍候</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="473"/> + <source>Error updating rule</source> + <translation>更新規則時出錯</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="508"/> + <source>Add at least one statement.</source> + <translation>至少新增一個語句。</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="519"/> + <source>Adding rule, wait</source> + <translation>正在新增規則,請稍候</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="529"/> + <source><select a statement></source> + <translation><選擇一個語句></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="654"/> + <source>num</source> + <translation>數字</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="668"/> + <source>to</source> + <translation>到</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="834"/> + <source>Equal</source> + <translation>等於</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="835"/> + <source>Not equal</source> + <translation>不等於</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="836"/> + <source>Greater or equal than</source> + <translation>大於或等於</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="837"/> + <source>Greater than</source> + <translation>大於</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="838"/> + <source>Less or equal than</source> + <translation>小於或等於</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="839"/> + <source>Less than</source> + <translation>小於</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1007"/> + <source>Warning: ct set mark value is empty, malformed rule?</source> + <translation>警告:ct 設定標記值為空,規則格式錯誤?</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1523"/> + <source>Firewall rule</source> + <translation>防火牆規則</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1059"/> + <source>Simple</source> + <translation>簡易</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1064"/> + <source>Advanced</source> + <translation>進階</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1186"/> + <source>This rule is not supported yet.</source> + <translation>此規則目前尚未支援。</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1251"/> + <source>Exclude service</source> + <translation>排除服務</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1263"/> + <source>Allow inbound connections to the selected port.</source> + <translation>允許到選定連接埠的對內連接。</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1265"/> + <source>Allow outbound connections to the selected port.</source> + <translation>允許到選定連接埠的對外連接。</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1341"/> + <source>select a statement.</source> + <translation>選擇一個語句。</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1357"/> + <source>value cannot be 0 or empty.</source> + <translation>值不能為 0 或空。</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1369"/> + <source>the value format is 1024/kbytes (or bytes, mbytes, gbytes)</source> + <translation>值的格式為 1024/kbytes (或 bytes, mbytes, gbytes)</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1383"/> + <source>the value format is 1024/kbytes/second (or bytes, mbytes, gbytes)</source> + <translation>值的格式為 1024/kbytes/second (或 bytes, mbytes, gbytes)</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1386"/> + <source>rate-limit not valid, use: bytes, kbytes, mbytes or gbytes.</source> + <translation>速率限制無效,使用:bytes, kbytes, mbytes 或 gbytes。</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1388"/> + <source>time-limit not valid, use: second, minute, hour or day</source> + <translation>時間限制無效,使用:second, minute, hour 或 day</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/firewall_rule.py" line="1455"/> + <source>port not valid.</source> + <translation>連接埠無效。</translation> + </message> +</context> +<context> + <name>messages</name> + <message> + <location filename="../../../opensnitch/service.py" line="301"/> + <source>Info</source> + <translation>資訊</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="305"/> + <source>Error</source> + <translation>錯誤</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="309"/> + <source>Warning</source> + <translation>警告</translation> + </message> +</context> +<context> + <name>notifications</name> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="767"/> + <source>System notifications are not available, you need to install python3-notify2.</source> + <translation>系統通知無法使用,您需要安裝 python3-notify2 套件。</translation> + </message> +</context> +<context> + <name>popups</name> + <message> + <location filename="../../../opensnitch/notifications.py" line="42"/> + <source>Open</source> + <translation>開啟</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="119"/> + <source>Allow</source> + <translation>允許</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="118"/> + <source>Deny</source> + <translation>拒絕</translation> + </message> + <message> + <location filename="../../../opensnitch/notifications.py" line="114"/> + <source>New outgoing connection</source> + <translation>新的對外連線</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="494"/> + <source>is connecting to <b>%s</b> on %s port %d</source> + <translation>正在連線到 <b>%s</b> 的 %s 連接埠 %d</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/> + <source>until reboot</source> + <translation>直到重新啟動</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="54"/> + <source>forever</source> + <translation>永久</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="120"/> + <source>Reject</source> + <translation>拒絕</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="335"/> + <source>Outgoing connection</source> + <translation>對外連線</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="340"/> + <source>Process launched from:</source> + <translation>處理程序起始來源:</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="377"/> + <source>from this executable</source> + <translation>來自此執行檔</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="381"/> + <source>from this command line</source> + <translation>來自此命令列</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="383"/> + <source>to port {0}</source> + <translation>到埠號 {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="446"/> + <source>to {0}</source> + <translation>到 {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="386"/> + <source>from user {0}</source> + <translation>來自使用者 {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="390"/> + <source>from this PID</source> + <translation>來自此 PID</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="403"/> + <source>to {0}.*</source> + <translation>到 {0}.*</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="456"/> + <source>to *.{0}</source> + <translation>到 *.{0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="490"/> + <source><b>Remote</b> process %s running on <b>%s</b></source> + <translation><b>遠端</b> 處理程序 %s 在 <b>%s</b> 上執行</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="501"/> + <source>is connecting to <b>%s</b>, %s</source> + <translation>正在連線到 <b>%s</b>,%s</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/prompt.py" line="506"/> + <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source> + <translation>正試圖透過 %s,%s 連接埠 %d 解析 <b>%s</b></translation> + </message> +</context> +<context> + <name>preferences</name> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="458"/> + <source>Warning</source> + <translation>警告</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="44"/> + <source>Restart the GUI in order changes to take effect</source> + <translation>重新啟動 GUI 以使變更生效</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="438"/> + <source>There're no nodes connected</source> + <translation>沒有任何節點已連線。</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="183"/> + <source>System default</source> + <translation>系統預設</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="534"/> + <source>System</source> + <translation>系統</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="206"/> + <source>Themes not available. Install qt-material: pip3 install qt-material</source> + <translation>主題不可用。安裝 qt-material:pip3 install qt-material</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="337"/> + <source>Server address can not be empty</source> + <translation>伺服器位址不能為空</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="368"/> + <source>Error loading {0} configuration</source> + <translation>載入 {0} 設定時出錯</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="436"/> + <source>Exception saving config: {0}</source> + <translation>儲存設定時發生例外:{0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="452"/> + <source>DB type changed</source> + <translation>DB 類型已變更</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="458"/> + <source>You must select a file for the database<br>or choose "In memory" type.</source> + <translation>您必須為資料庫選擇一個檔案<br>或是選擇「記憶體中」的類型。</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="490"/> + <source>Certificates changed</source> + <translation>憑證已變更</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="504"/> + <source>Language changed</source> + <translation>語言已變更</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="535"/> + <source>UI theme changed</source> + <translation>UI 主題已變更</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="565"/> + <source>Applying configuration on {0} ...</source> + <translation>正在對 {0} 套用設定 ...</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="573"/> + <source>Ok</source> + <translation>確定</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="583"/> + <source>Exception saving node config {0}: {1}</source> + <translation>儲存節點設定 {0} 時發生例外:{1}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="594"/> + <source>Certs fields cannot be empty.</source> + <translation>憑證欄位不能為空。</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="597"/> + <source>cert file has excessive permissions, it should have 0600</source> + <translation>憑證檔案權限過大,應設為 0600</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="601"/> + <source>cert key file has excessive permissions, it should have 0600</source> + <translation>憑證金鑰檔案權限過大,應設為 0600</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="607"/> + <source>CA cert file has excessive permissions, it should have 0600</source> + <translation>CA 憑證檔案權限過大,應設為 0600</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="667"/> + <source>Configuration applied.</source> + <translation>設定已套用。</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="669"/> + <source>Error applying configuration: {0}</source> + <translation>套用設定時出錯:{0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="674"/> + <source>Certs changed</source> + <translation>憑證已變更</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="710"/> + <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source> + <translation>將滑鼠停在文字上以顯示幫助<br><br>別忘了造訪 wiki:<a href="{0}">{0}</a></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/preferences.py" line="737"/> + <source>Auth type changed</source> + <translation>認證類型已變更</translation> + </message> +</context> +<context> + <name>proc_details</name> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="100"/> + <source><b>Error loading process information:</b> <br><br> + +</source> + <translation><b>載入處理程序資訊時出錯:</b> <br><br> + +</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="119"/> + <source><b>Error stopping monitoring process:</b><br><br></source> + <translation><b>停止監控處理程序時出錯:</b><br><br></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/processdetails.py" line="159"/> + <source>loading...</source> + <translation>載入中...</translation> + </message> +</context> +<context> + <name>rules</name> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="238"/> + <source>There're no nodes connected.</source> + <translation>沒有已連線的節點。</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="255"/> + <source>There's already a rule with this name.</source> + <translation>已經有一條相同名稱的規則。</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="281"/> + <source>Rule applied.</source> + <translation>規則已套用。</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="283"/> + <source>Error applying rule: {0}</source> + <translation>套用規則出錯:{0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="549"/> + <source><b>Error loading rule</b></source> + <translation><b>載入規則出錯</b></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="651"/> + <source>protocol can not be empty, or uncheck it</source> + <translation>通訊協定不能為空或取消勾選</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="665"/> + <source>Protocol regexp error</source> + <translation>通訊協定正規表達式錯誤</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="669"/> + <source>process path can not be empty</source> + <translation>處理程序路徑不能為空</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="683"/> + <source>Process path regexp error</source> + <translation>處理程序路徑正規表達式錯誤</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="687"/> + <source>command line can not be empty</source> + <translation>命令列不能為空</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="701"/> + <source>Command line regexp error</source> + <translation>命令列正規表達式錯誤</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="705"/> + <source>Network interface can not be empty</source> + <translation>網路介面不能為空</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="719"/> + <source>Network interface regexp error</source> + <translation>網路介面正規表達式錯誤</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="723"/> + <source>Source port can not be empty</source> + <translation>來源連接埠不能為空</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="737"/> + <source>Source port regexp error</source> + <translation>來源連接埠正規表達式錯誤</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="741"/> + <source>Dest port can not be empty</source> + <translation>目標連接埠不能為空</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="755"/> + <source>Dst port regexp error</source> + <translation>目標連接埠正規表達式錯誤</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="759"/> + <source>Dest host can not be empty</source> + <translation>目標主機不能為空</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="773"/> + <source>Dst host regexp error</source> + <translation>目標主機正規表達式錯誤</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="777"/> + <source>Source IP/Network can not be empty</source> + <translation>來源 IP/網路不能為空</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="803"/> + <source>Source IP regexp error</source> + <translation>來源 IP 正規表達式錯誤</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="815"/> + <source>Dest IP/Network can not be empty</source> + <translation>目標 IP/網路不能為空</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="841"/> + <source>Dst IP regexp error</source> + <translation>目標 IP 正規表達式錯誤</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="856"/> + <source>User ID can not be empty</source> + <translation>使用者 ID 不能為空</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="873"/> + <source>User ID regexp error</source> + <translation>使用者 ID 正規表達式錯誤</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="876"/> + <source>Invalid UID, it must be a digit.</source> + <translation>無效的 UID,必須是數字。</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="890"/> + <source>PID field can not be empty</source> + <translation>PID 欄位不能為空</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="904"/> + <source>PID field regexp error</source> + <translation>PID 欄位正規表達式錯誤</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="960"/> + <source>Lists field cannot be empty</source> + <translation>列表欄位不能為空</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="962"/> + <source>Lists field must be a directory</source> + <translation>列表欄位必須是目錄</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="992"/> + <source>Select at least one field.</source> + <translation>至少選擇一個欄位。</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="1005"/> + <source><b>Rule not supported</b></source> + <translation><b>不支援的規則</b></translation> + </message> +</context> +<context> + <name>stats</name> + <message> + <location filename="../../../opensnitch/service.py" line="231"/> + <source>WARNING</source> + <translation>警告</translation> + </message> + <message> + <location filename="../../../opensnitch/service.py" line="796"/> + <source>New node connected</source> + <translation>新節點已連接</translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="17"/> + <source>What</source> + <translation>什麼</translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="18"/> + <source>Hits</source> + <translation>命中次數</translation> + </message> + <message> + <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="19"/> + <source>Network name</source> + <translation>網路名稱</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="293"/> + <source>Time</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>時間</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="297"/> + <source>Node</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>節點</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="294"/> + <source>Action</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>動作</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="304"/> + <source>Destination</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>目的地</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="301"/> + <source>Protocol</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>協議</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="302"/> + <source>Process</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>處理程序</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="308"/> + <source>Rule</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>規則</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="286"/> + <source>Name</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>名稱</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="287"/> + <source>Address</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>位址</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="288"/> + <source>Status</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>狀態</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="289"/> + <source>Hostname</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>主機名稱</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="290"/> + <source>Uptime</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>運作時間</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="422"/> + <source>Version</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>版本</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="419"/> + <source>Rules</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>規則</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="295"/> + <source>Duration</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>持續時間</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="296"/> + <source>Description</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>描述</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="298"/> + <source>Enabled</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>已啟用</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="299"/> + <source>Precedence</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>優先順序</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="437"/> + <source>Hits</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>命中次數</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="303"/> + <source>Cmdline</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>命令列</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="305"/> + <source>DstIP</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>目標 IP</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="306"/> + <source>DstHost</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>目標主機</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="307"/> + <source>DstPort</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>目標連接埠</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="309"/> + <source>UserID</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>使用者 ID</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="310"/> + <source>LastConnection</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>最後連線</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="312"/> + <source>Not running</source> + <translation>未運作</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="313"/> + <source>Disabled</source> + <translation>已停用</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="314"/> + <source>Running</source> + <translation>運作中</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="405"/> + <source>Export rules</source> + <translation>匯出規則</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="406"/> + <source>Import rules</source> + <translation>匯入規則</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="407"/> + <source>Export events to CSV</source> + <translation>將事件匯出至 CSV</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="408"/> + <source>Quit</source> + <translation>退出</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="420"/> + <source>Connections</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>連線</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="421"/> + <source>Dropped</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>已丟棄</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="436"/> + <source>What</source> + <comment>This is a word, without spaces and symbols.</comment> + <translation>內容</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="635"/> + <source>OpenSnitch Network Statistics {0}</source> + <translation>OpenSnitch 網路統計 {0}</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="637"/> + <source>OpenSnitch Network Statistics for {0}</source> + <translation>OpenSnitch 為 {0} 的網路統計</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="832"/> + <source>Details</source> + <translation>詳細資訊</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="833"/> + <source>Rules</source> + <translation>規則</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="834"/> + <source>New</source> + <translation>新增</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="942"/> + <source>Export</source> + <translation>匯出</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="875"/> + <source>Action</source> + <translation>動作</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="964"/> + <source>Disable</source> + <translation>停用</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="966"/> + <source>Enable</source> + <translation>啟用</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="971"/> + <source>Delete</source> + <translation>刪除</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="970"/> + <source>Edit</source> + <translation>編輯</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="974"/> + <source>To clipboard</source> + <translation>複製到剪貼簿</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="941"/> + <source>Apply to</source> + <translation>套用於</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="950"/> + <source>Allow</source> + <translation>允許</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="951"/> + <source>Deny</source> + <translation>阻擋</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="952"/> + <source>Reject</source> + <translation>拒絕</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="955"/> + <source>Always</source> + <translation>總是</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="956"/> + <source>Until reboot</source> + <translation>持續到重新啟動</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="969"/> + <source>Duplicate</source> + <translation>複製</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="975"/> + <source>To disk</source> + <translation>儲存到磁碟</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1730"/> + <source> Are you sure?</source> + <translation> 您確定嗎?</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2559"/> + <source>Select a directory to export rules</source> + <translation>選擇一個目錄以匯出規則</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1207"/> + <source> Your are about to delete this rule. </source> + <translation> 您即將刪除此規則。 </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1209"/> + <source> Your are about to delete this entry. </source> + <translation> 您即將刪除此條目。 </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1266"/> + <source>Rule not found by that name and node</source> + <translation>未找到該名稱和節點的規則</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1319"/> + <source>Error:</source> + <translation>錯誤:</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1327"/> + <source>Warning:</source> + <translation>警告:</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1697"/> + <source> You are about to delete this node. </source> + <translation> 您即將刪除此節點。 </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1706"/> + <source><b>Error deleting node</b><br><br></source> + <comment>{0}</comment> + <translation><b>刪除節點時出錯</b><br><br></translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="1730"/> + <source> You are about to delete this rule. </source> + <translation> 您即將刪除此規則。 </translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2514"/> + <source>Error exporting rules</source> + <translation>匯出規則時出錯</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2588"/> + <source>Select a directory with rules to import (JSON files)</source> + <translation>選擇一個含有要匯入的規則的目錄(JSON 檔案)</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2602"/> + <source>Rules imported fine</source> + <translation>規則匯入成功</translation> + </message> + <message> + <location filename="../../../opensnitch/dialogs/stats.py" line="2617"/> + <source>Save as CSV</source> + <translation>另存為 CSV</translation> + </message> +</context> +</TS> diff --git a/ui/i18n/opensnitch_i18n.pro b/ui/i18n/opensnitch_i18n.pro new file mode 100644 index 0000000..6732729 --- /dev/null +++ b/ui/i18n/opensnitch_i18n.pro @@ -0,0 +1,40 @@ +#TEMPLATE = app +#TARGET = ts +#INCLUDEPATH += opensnitch + + +# Input +SOURCES += ../opensnitch/service.py \ + ../opensnitch/notifications.py \ + ../opensnitch/customwidgets/addresstablemodel.py \ + ../opensnitch/customwidgets/main.py \ + ../opensnitch/dialogs/prompt.py \ + ../opensnitch/dialogs/preferences.py \ + ../opensnitch/dialogs/ruleseditor.py \ + ../opensnitch/dialogs/processdetails.py \ + ../opensnitch/dialogs/stats.py \ + ../opensnitch/dialogs/firewall.py \ + ../opensnitch/dialogs/firewall_rule.py + +FORMS += ../opensnitch/res/prompt.ui \ + ../opensnitch/res/ruleseditor.ui \ + ../opensnitch/res/preferences.ui \ + ../opensnitch/res/process_details.ui \ + ../opensnitch/res/stats.ui \ + ../opensnitch/res/firewall.ui \ + ../opensnitch/res/firewall_rule.ui +TRANSLATIONS += locales/de_DE/opensnitch-de_DE.ts \ + locales/es_ES/opensnitch-es_ES.ts \ + locales/eu_ES/opensnitch-eu_ES.ts \ + locales/hu_HU/opensnitch-hu_HU.ts \ + locales/ja_JP/opensnitch-ja_JP.ts \ + locales/pt_BR/opensnitch-pt_BR.ts \ + locales/ro_RO/opensnitch-ro_RO.ts \ + locales/fr_FR/opensnitch-fr_FR.ts \ + locales/lt_LT/opensnitch-lt_LT.ts \ + locales/tr_TR/opensnitch-tr_TR.ts \ + locales/ru_RU/opensnitch-ru_RU.ts \ + locales/nb_NO/opensnitch-nb_NO.ts \ + locales/nl_NL/opensnitch-nl_NL.ts \ + locales/fi_FI/opensnitch-fi_FI.ts \ + locales/zh_TW/opensnitch-zh_TW.ts diff --git a/ui/opensnitch/__init__.py b/ui/opensnitch/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ui/opensnitch/actions/__init__.py b/ui/opensnitch/actions/__init__.py new file mode 100644 index 0000000..3b145e7 --- /dev/null +++ b/ui/opensnitch/actions/__init__.py @@ -0,0 +1,159 @@ +from PyQt5.QtCore import QObject + +import json +import os +import glob +import sys + +from opensnitch.utils.xdg import xdg_config_home +from opensnitch.actions import highlight +from opensnitch.actions.default_configs import commonDelegateConfig, rulesDelegateConfig, fwDelegateConfig + +class Actions(QObject): + """List of actions to perform on the data that is displayed on the GUI. + Whenever an item matches a condition an action is applied, for example: + - if the text of a cell matches a condition for the given columns, + then the properties of the cell/row and the text are customized. + + There's only 1 action supported right now: + - highlight: for customizing rows and cells appearance. + + There're 3 actions by default of type Highlight: + - rules: applied to the rules to colorize the columns Enabled and + Action + - firewall: applied to the fw rules to colorize the columns Action and + Target. + - common: applied to the rest of the views to colorize the column + Action. + + Users can modify the default actions, by adding more patterns to colorize. + At the same time they can also create new actions to be applied on certain views. + + The format of the actions is JSON: + { + "created": "....", + "name": "...", + "actions": { + "highlight": { + "cells": [ + { + "text": ["allow", "True", "online"], + "cols": [3,5,6], + "color": "green", + }, + { + "text": ["deny", "False", "offline"], + "cols": [3,5,6], + "color": "red", + } + ], + "rows": [] + } + } + + """ + __instance = None + + # list of loaded actions + _actions = None + + + KEY_ACTIONS = "actions" + KEY_NAME = "name" + KEY_TYPE = "type" + + # TODO: emit a signal when the actions are (re)loaded + # reloaded_signal = pyQtSignal() + + # default paths to look for actions + _paths = [ + os.path.dirname(sys.modules[__name__].__file__) + "/data/", + "{0}/{1}".format(xdg_config_home, "/opensnitch/actions/") + ] + + @staticmethod + def instance(): + if Actions.__instance == None: + Actions.__instance = Actions() + return Actions.__instance + + def __init__(self, parent=None): + QObject.__init__(self) + self._actions_list = {} + try: + base_dir = "{0}/{1}".format(xdg_config_home, "/opensnitch/actions/") + os.makedirs(base_dir, 0o700) + except: + pass + + def _load_default_configs(self): + self._actions_list[commonDelegateConfig[Actions.KEY_NAME]] = self.compile(commonDelegateConfig) + self._actions_list[rulesDelegateConfig[Actions.KEY_NAME]] = self.compile(rulesDelegateConfig) + self._actions_list[fwDelegateConfig[Actions.KEY_NAME]] = self.compile(fwDelegateConfig) + + def loadAll(self): + """look for actions firstly on default system path, secondly on + user's home. + If a user customizes existing configurations, they'll be saved under + the user's home directory. + + Action files are .json files. + """ + self._load_default_configs() + + for path in self._paths: + for jfile in glob.glob(os.path.join(path, '*.json')): + self.load(jfile) + + def load(self, action_file): + """read a json file from disk and create the action.""" + with open(action_file, 'r') as fd: + data=fd.read() + obj = json.loads(data) + self._actions_list[obj[Actions.KEY_NAME]] = self.compile(obj) + + def compile(self, obj): + try: + if Actions.KEY_NAME not in obj or obj[Actions.KEY_NAME] == "": + return None + if obj.get(Actions.KEY_ACTIONS) == None: + return None + + for action in obj[Actions.KEY_ACTIONS]: + if action == highlight.Highlight.NAME: + h = highlight.Highlight(obj[Actions.KEY_ACTIONS][action]) + h.compile() + obj[Actions.KEY_ACTIONS][action]= h + else: + print("Actions exception: Action '{0}' not supported yet".format(obj[Actions.KEY_NAME])) + + return obj + except Exception as e: + print("Actions.compile() exception:", e) + return None + + + + def getAll(self): + return self._actions_list + + def deleteAll(self): + self._actions_list = {} + + def get(self, name): + try: + return self._actions_list[name] + except Exception as e: + print("get() exception:", e) + return None + + def delete(self, name): + try: + del self._actions_list[name] + # TODO: + # self.reloaded_signal.emit() + except: + pass + + def isValid(self): + pass diff --git a/ui/opensnitch/actions/default_configs.py b/ui/opensnitch/actions/default_configs.py new file mode 100644 index 0000000..fae8b8b --- /dev/null +++ b/ui/opensnitch/actions/default_configs.py @@ -0,0 +1,128 @@ + +# common configuration to highlight Action column +commonDelegateConfig = { + "name": "commonDelegateConfig", + "created": "", + "updated": "", + "actions": { + "highlight": { + "cells": [ + { + "text": ["allow", "\u2713 online"], + "operator": "==", + "cols": [1, 2, 3], + "color": "green", + "bgcolor": "", + "alignment": ["center"] + }, + { + "text": ["deny", "\u2613 offline"], + "cols": [1, 2, 3], + "color": "red", + "bgcolor": "", + "alignment": ["center"] + }, + { + "text": ["reject"], + "cols": [1, 2, 3], + "color": "purple", + "bgcolor": "", + "alignment": ["center"] + } + ], + "rows": [] + } + } +} + +# firewall rules configuration to highlight Enabled and Action columns +fwDelegateConfig = { + "name": "defaultFWDelegateConfig", + "created": "", + "updated": "", + "actions": { + "highlight": { + "cells": [ + { + "text": [ + "allow", + "True", + "accept", + "jump", + "masquerade", + "snat", + "dnat", + "tproxy", + "queue", + "redirect", + "True", + "ACCEPT" + ], + "cols": [7, 10], + "color": "green", + "bgcolor": "", + "alignment": ["center"] + }, + { + "text": [ + "deny", + "False", + "drop", + "DROP", + "stop" + ], + "cols": [7, 10], + "color": "red", + "bgcolor": "", + "alignment": ["center"] + }, + { + "text": [ + "reject", + "return" + ], + "cols": [7, 10], + "color": "purple", + "bgcolor": "", + "alignment": ["center"] + } + ], + "rows": [] + } + } +} + +# rules configuration to highlight Enabled and Action columns +rulesDelegateConfig = { + "name": "defaultRulesDelegateConfig", + "created": "", + "updated": "", + "actions": { + "highlight": { + "cells": [ + { + "text": ["allow", "True"], + "cols": [3, 4], + "color": "green", + "bgcolor": "", + "alignment": ["center"] + }, + { + "text": ["deny", "False"], + "cols": [3, 4], + "color": "red", + "bgcolor": "", + "alignment": ["center"] + }, + { + "text": ["reject"], + "cols": [3, 4], + "color": "purple", + "bgcolor": "", + "alignment": ["center"] + } + ], + "rows": [] + } + } +} diff --git a/ui/opensnitch/actions/highlight.py b/ui/opensnitch/actions/highlight.py new file mode 100644 index 0000000..c76a810 --- /dev/null +++ b/ui/opensnitch/actions/highlight.py @@ -0,0 +1,236 @@ +from PyQt5 import Qt, QtCore +from PyQt5.QtGui import QColor, QStandardItemModel, QStandardItem + +# PyQt5 >= v5.15.8 (#821) +if hasattr(Qt, 'QStyle'): + from PyQt5.Qt import QStyle +else: + from PyQt5.QtWidgets import QStyle + +class Highlight(): + """Customizes QTablewView cells via QItemDelegates. + Format: + [ + { + 'text': {"allow", "True", "online"}, + 'cols': {1,4,5}, + 'color': "green", + 'bgcolor': None, + 'alignment': ["center"], + #"margins': [0, 0] + #'font': {} + }, + ] + + text: will match any of the given texts. + cols: look for patterns on these columns. + color: colorizes the color of the text. + bgcolor: colorizes the background color of the cell. + etc. + """ + + NAME = "highlight" + + MARGINS = "margins" + ALIGNMENT = "alignment" + # QtCore.Qt.AlignCenter + ALIGN_CENTER = "center" + # QtCore.Qt.AlignHCenter + ALIGN_HCENTER = "hcenter" + # QtCore.Qt.AlignVCenter + ALIGN_VCENTER = "vcenter" + + COLOR = "color" + BGCOLOR = "bgcolor" + FONT = "font" + CELLS = "cells" + ROWS = "rows" + COLS = "cols" + TEXT = "text" + + def __init__(self, config): + # original json config received + self._config = config + self._last_visited_row = -1 + self._rowcells = "" + + def compile(self): + """transform json items to Qt objects. + These items are transformed: + - color (to QColor), bgcolor (to QColor), alignment (to Qt.Align*), + font (to QFont TODO) + + Return the original json object transformed. + """ + # cells, rows + for idx in self._config: + cells = self._config[idx] + for cell in cells: + for item in cell: + # colors + if (item == Highlight.COLOR or item == Highlight.BGCOLOR): + if cell[item] != "" and cell[item] is not None: + cell[item] = QColor(cell[item]) + else: + cell[item] = None + + # alignments + if item == Highlight.ALIGNMENT: + cell[item] = self.getAlignment(cell[item]) + + # fonts + if item == Highlight.FONT: + self.getFont(cell[item]) + + return self._config + + + def run(self, args): + """Highlight cells or rows based on patterns. + + Return if the cell was modified. + + Keyword arguments: + args -- tuple of options. + """ + painter = args[0] + option = args[1] + index = args[2] + style = args[3] + modelColumns = args[4] + curRow = args[5] + curColumn = args[6] + defaultPen = args[7] + defaultBrush = args[8] + cellAlignment = args[9] + cellRect = args[10] + cellValue = args[11] + + # signal that this cell has been modified + modified = False + + cells = self._config.get(Highlight.CELLS) + rows = self._config.get(Highlight.ROWS) + + if cells: + for cell in cells: + if curColumn not in cell[Highlight.COLS]: + continue + if cellValue not in cell[Highlight.TEXT]: + continue +# TODO +# if cell['operator'] == 'simple' and cellValue != cell['text']: +# continue +# elif cell['text'] not in cellValue: +# continue + + cellColor = cell.get(Highlight.COLOR) + cellBgColor = cell.get(Highlight.BGCOLOR) + if cell.get(Highlight.ALIGNMENT) != None: + cellAlignment = cell[Highlight.ALIGNMENT] + if cell.get(Highlight.MARGINS) != None: + cellRect.adjust( + int(cell[Highlight.MARGINS][self.HMARGIN]), + int(cell[Highlight.MARGINS][self.VMARGIN]), + -defaultPen.width(), + -defaultPen.width() + ) + + modified=True + self.paintCell( + style, + painter, + option, + defaultPen, + cellAlignment, + cellRect, + cellColor, + cellBgColor, + cellValue) + + if len(rows) == 0: + return (modified,) + + # get row's cells only for the first cell of the row, + # then reuse them for the rest of the cells of the current row. + if curRow != self._last_visited_row: + self._rowcells = " ".join( + [index.sibling(curRow, col).data() for col in range(0, modelColumns)] + ) + self._last_visited_row = curRow + + for row in rows: + skip = True + for text in row[Highlight.TEXT]: + if text in self._rowcells: + skip = False + if skip: + continue + + cellColor = row.get(Highlight.COLOR) + cellBgColor = row.get(Highlight.BGCOLOR) + if row.get(Highlight.ALIGNMENT) != None: + cellAlignment = row[Highlight.ALIGNMENT] + if row.get(Highlight.MARGINS) != None: + cellRect.adjust( + int(row[Highlight.MARGINS][self.HMARGIN]), + int(row[Highlight.MARGINS][self.VMARGIN]), + -defaultPen.width(), + -defaultPen.width() + ) + + modified=True + self.paintCell( + style, + painter, + option, + defaultPen, + cellAlignment, + cellRect, + cellColor, + cellBgColor, + cellValue) + + return (modified,) + + def paintCell(self, style, painter, option, defaultPen, cellAlignment, cellRect, cellColor, cellBgColor, cellValue): + cellSelected = option.state & QStyle.State_Selected + + painter.save() + # don't customize selected state + if not cellSelected: + if cellBgColor != None: + painter.fillRect(option.rect, cellBgColor) + + if cellColor is not None: + defaultPen.setColor(cellColor) + painter.setPen(defaultPen) + + # setting option.displayAlignment has no effect here, so we need to + # draw the text. + # FIXME: Drawing the text though, the background color of the SelectedState is + # altered. + # If we called super().paint(), modifying option.palette.* would be + # enough to change the text color, but it wouldn't be aligned: + # option.palette.setColor(QPalette.Text, cellColor) + style.drawItemText(painter, cellRect, cellAlignment, option.palette, True, cellValue) + painter.restore() + + def getAlignment(self, alignments): + alignFlags = 0 + for align in alignments: + if align == Highlight.ALIGN_CENTER: + alignFlags |= QtCore.Qt.AlignCenter + elif align == Highlight.ALIGN_HCENTER: + alignFlags |= QtCore.Qt.AlignHCenter + elif align == Highlight.ALIGN_VCENTER: + alignFlags |= QtCore.Qt.AlignVCenter + + if alignFlags == 0: + return None + + return alignFlags + + def getFont(self, font): + # TODO + pass diff --git a/ui/opensnitch/actions/utils.py b/ui/opensnitch/actions/utils.py new file mode 100644 index 0000000..5c435be --- /dev/null +++ b/ui/opensnitch/actions/utils.py @@ -0,0 +1,8 @@ +from PyQt5.QtGui import QColor + +def getColorNames(): + """Return the built-in color names that can be used to choose new colors: + https://doc.qt.io/qtforpython-5/PySide2/QtGui/QColor.html#predefined-colors + https://www.w3.org/TR/SVG11/types.html#ColorKeywords + """ + return QColor.colorNames() diff --git a/ui/opensnitch/auth/__init__.py b/ui/opensnitch/auth/__init__.py new file mode 100644 index 0000000..afd762b --- /dev/null +++ b/ui/opensnitch/auth/__init__.py @@ -0,0 +1,42 @@ + +import grpc + +Simple = "simple" +TLSSimple = "tls-simple" +TLSMutual = "tls-mutual" + +NO_CLIENT_CERT = "no-client-cert" +REQ_CERT = "req-cert" +REQ_ANY_CERT = "req-any-cert" +VERIFY_CERT = "verify-cert" +REQ_AND_VERIFY_CERT = "req-and-verify-cert" + + +def load_file(file_path): + try: + with open(file_path, "rb") as f: + return f.read() + except Exception as e: + print("auth: error loading {0}: {1}".format(file_path, e)) + + return None + + +def get_tls_credentials(ca_cert, server_cert, server_key): + """return a new gRPC credentials object given a server cert and key file. + https://grpc.io/docs/guides/auth/#python + """ + try: + cacert = load_file(ca_cert) + cert = load_file(server_cert) + cert_key = load_file(server_key) + auth_nodes = False if cacert == None else True + + return grpc.ssl_server_credentials( + ((cert_key, cert),), + cacert, + auth_nodes + ) + except Exception as e: + print("get_tls_credentials error:", e) + return None diff --git a/ui/opensnitch/config.py b/ui/opensnitch/config.py new file mode 100644 index 0000000..dd61f56 --- /dev/null +++ b/ui/opensnitch/config.py @@ -0,0 +1,255 @@ +from PyQt5 import QtCore +from opensnitch.database import Database + +class Config: + __instance = None + + HELP_URL = "https://github.com/evilsocket/opensnitch/wiki/" + HELP_RULES_URL = "https://github.com/evilsocket/opensnitch/wiki/Rules" + HELP_SYS_RULES_URL = "https://github.com/evilsocket/opensnitch/wiki/System-rules#upgrading-from-previous-versions" + HELP_SYSFW_URL = "https://github.com/evilsocket/opensnitch/wiki/System-rules" + HELP_CONFIG_URL = "https://github.com/evilsocket/opensnitch/wiki/Configurations" + HELP_SYSTRAY_WARN = "https://github.com/evilsocket/opensnitch/wiki/GUI-known-problems#gui-does-not-show-up" + + OPERAND_PROCESS_ID = "process.id" + OPERAND_PROCESS_PATH = "process.path" + OPERAND_PROCESS_COMMAND = "process.command" + OPERAND_PROCESS_ENV = "process.env." + OPERAND_USER_ID = "user.id" + OPERAND_IFACE_OUT = "iface.out" + OPERAND_IFACE_IN = "iface.in" + OPERAND_SOURCE_IP = "source.ip" + OPERAND_SOURCE_PORT = "source.port" + OPERAND_DEST_IP = "dest.ip" + OPERAND_DEST_HOST = "dest.host" + OPERAND_DEST_PORT = "dest.port" + OPERAND_DEST_NETWORK = "dest.network" + OPERAND_SOURCE_NETWORK = "source.network" + OPERAND_PROTOCOL = "protocol" + OPERAND_LIST_DOMAINS = "lists.domains" + OPERAND_LIST_DOMAINS_REGEXP = "lists.domains_regexp" + OPERAND_LIST_IPS = "lists.ips" + OPERAND_LIST_NETS = "lists.nets" + + RULE_TYPE_LIST = "list" + RULE_TYPE_LISTS = "lists" + RULE_TYPE_SIMPLE = "simple" + RULE_TYPE_REGEXP = "regexp" + RULE_TYPE_NETWORK = "network" + RulesTypes = (RULE_TYPE_LIST, RULE_TYPE_LISTS, RULE_TYPE_SIMPLE, RULE_TYPE_REGEXP, RULE_TYPE_NETWORK) + + DEFAULT_TARGET_PROCESS = 0 + ACTION_DENY_IDX = 0 + ACTION_ALLOW_IDX = 1 + ACTION_REJECT_IDX = 2 + + # don't translate + ACTION_ALLOW = "allow" + ACTION_DENY = "deny" + ACTION_REJECT = "reject" + ACTION_ACCEPT = "accept" + ACTION_DROP = "drop" + ACTION_JUMP = "jump" + ACTION_REDIRECT = "redirect" + ACTION_RETURN = "return" + ACTION_TPROXY = "tproxy" + ACTION_SNAT = "snat" + ACTION_DNAT = "dnat" + ACTION_MASQUERADE = "masquerade" + ACTION_QUEUE = "queue" + ACTION_LOG = "log" + ACTION_STOP = "stop" + + DURATION_FIELD = "duration" + DURATION_UNTIL_RESTART = "until restart" + DURATION_ALWAYS = "always" + DURATION_ONCE = "once" + DURATION_1h = "1h" + DURATION_30m = "30m" + DURATION_15m = "15m" + DURATION_5m = "5m" + DURATION_30s = "30s" + + # Rules of this list are ignored/deleted + RULES_DURATION_FILTER = () + # Rules of this list are active + RULES_ACTIVE_TEMPORARY_RULES = () + RULES_TEMPORARY_LIST = [ + DURATION_ONCE, DURATION_30s, DURATION_5m, + DURATION_15m, DURATION_30m, DURATION_1h, + DURATION_UNTIL_RESTART] + + DEFAULT_DURATION_IDX = 6 # until restart + + POPUP_CENTER = 0 + POPUP_TOP_RIGHT = 1 + POPUP_BOTTOM_RIGHT = 2 + POPUP_TOP_LEFT = 3 + POPUP_BOTTOM_LEFT = 4 + + DEFAULT_THEME = "global/theme" + DEFAULT_THEME_DENSITY_SCALE = "global/theme_density_scale" + DEFAULT_LANGUAGE = "global/language" + DEFAULT_LANGNAME = "global/langname" + DEFAULT_DISABLE_POPUPS = "global/disable_popups" + DEFAULT_TIMEOUT_KEY = "global/default_timeout" + DEFAULT_ACTION_KEY = "global/default_action" + DEFAULT_DURATION_KEY = "global/default_duration" + DEFAULT_TARGET_KEY = "global/default_target" + DEFAULT_IGNORE_RULES = "global/default_ignore_rules" + DEFAULT_IGNORE_TEMPORARY_RULES = "global/default_ignore_temporary_rules" + DEFAULT_POPUP_POSITION = "global/default_popup_position" + DEFAULT_POPUP_ADVANCED = "global/default_popup_advanced" + DEFAULT_POPUP_ADVANCED_DSTIP = "global/default_popup_advanced_dstip" + DEFAULT_POPUP_ADVANCED_DSTPORT = "global/default_popup_advanced_dstport" + DEFAULT_POPUP_ADVANCED_UID = "global/default_popup_advanced_uid" + DEFAULT_SERVER_ADDR = "global/server_address" + DEFAULT_SERVER_MAX_MESSAGE_LENGTH = "global/server_max_message_length" + DEFAULT_HIDE_SYSTRAY_WARN = "global/hide_systray_warning" + DEFAULT_DB_TYPE_KEY = "database/type" + DEFAULT_DB_FILE_KEY = "database/file" + DEFAULT_DB_PURGE_OLDEST = "database/purge_oldest" + DEFAULT_DB_MAX_DAYS = "database/max_days" + DEFAULT_DB_PURGE_INTERVAL = "database/purge_interval" + DEFAULT_DB_JRNL_WAL = "database/jrnl_wal" + + DEFAULT_TIMEOUT = 30 + + NOTIFICATIONS_ENABLED = "notifications/enabled" + NOTIFICATIONS_TYPE = "notifications/type" + NOTIFICATION_TYPE_SYSTEM = 0 + NOTIFICATION_TYPE_QT = 1 + + STATS_REFRESH_INTERVAL = "statsDialog/refresh_interval" + STATS_GEOMETRY = "statsDialog/geometry" + STATS_LAST_TAB = "statsDialog/last_tab" + STATS_FILTER_TEXT = "statsDialog/general_filter_text" + STATS_FILTER_ACTION = "statsDialog/general_filter_action" + STATS_LIMIT_RESULTS = "statsDialog/general_limit_results" + STATS_SHOW_COLUMNS = "statsDialog/show_columns" + STATS_NODES_COL_STATE = "statsDialog/nodes_columns_state" + STATS_GENERAL_COL_STATE = "statsDialog/general_columns_state" + STATS_GENERAL_FILTER_TEXT = "statsDialog/" + STATS_GENERAL_FILTER_ACTION = "statsDialog/" + STATS_RULES_COL_STATE = "statsDialog/rules_columns_state" + STATS_FW_COL_STATE = "statsDialog/firewall_columns_state" + STATS_RULES_TREE_EXPANDED_0 = "statsDialog/rules_tree_0_expanded" + STATS_RULES_TREE_EXPANDED_1 = "statsDialog/rules_tree_1_expanded" + STATS_RULES_SPLITTER_POS = "statsDialog/rules_splitter_pos" + STATS_VIEW_COL_STATE = "statsDialog/view_columns_state" + STATS_VIEW_DETAILS_COL_STATE = "statsDialog/view_details_columns_state" + + QT_PLATFORM_PLUGIN = "global/qt_platform_plugin" + QT_AUTO_SCREEN_SCALE_FACTOR = "global/screen_scale_factor_auto" + QT_SCREEN_SCALE_FACTOR = "global/screen_scale_factor" + + INFOWIN_GEOMETRY = "infoWindow/geometry" + + AUTH_TYPE = "auth/type" + AUTH_CA_CERT = "auth/cacert" + AUTH_CERT = "auth/cert" + AUTH_CERTKEY = "auth/certkey" + # don't translate + + @staticmethod + def init(): + Config.__instance = Config() + return Config.__instance + + @staticmethod + def get(): + if Config.__instance == None: + Config._instance = Config() + return Config.__instance + + def __init__(self): + self.settings = QtCore.QSettings("opensnitch", "settings") + + if self.settings.value(self.DEFAULT_TIMEOUT_KEY) == None: + self.setSettings(self.DEFAULT_TIMEOUT_KEY, self.DEFAULT_TIMEOUT) + if self.settings.value(self.DEFAULT_ACTION_KEY) == None: + self.setSettings(self.DEFAULT_ACTION_KEY, self.ACTION_DENY_IDX) + if self.settings.value(self.DEFAULT_DURATION_KEY) == None: + self.setSettings(self.DEFAULT_DURATION_KEY, self.DEFAULT_DURATION_IDX) + if self.settings.value(self.DEFAULT_TARGET_KEY) == None: + self.setSettings(self.DEFAULT_TARGET_KEY, self.DEFAULT_TARGET_PROCESS) + if self.settings.value(self.DEFAULT_DB_TYPE_KEY) == None: + self.setSettings(self.DEFAULT_DB_TYPE_KEY, Database.DB_TYPE_MEMORY) + self.setSettings(self.DEFAULT_DB_FILE_KEY, Database.DB_IN_MEMORY) + self.setSettings(self.DEFAULT_DB_JRNL_WAL, Database.DB_JRNL_WAL) + + self.setRulesDurationFilter( + self.getBool(self.DEFAULT_IGNORE_RULES), + self.getInt(self.DEFAULT_IGNORE_TEMPORARY_RULES) + ) + + def reload(self): + self.settings = QtCore.QSettings("opensnitch", "settings") + + def hasKey(self, key): + return self.settings.contains(key) + + def setSettings(self, path, value): + self.settings.setValue(path, value) + self.settings.sync() + + def getSettings(self, path): + return self.settings.value(path) + + def getBool(self, path, default_value=False): + return self.settings.value(path, type=bool, defaultValue=default_value) + + def getInt(self, path, default_value=0): + try: + return self.settings.value(path, type=int, defaultValue=default_value) + except Exception: + return default_value + + def getDefaultAction(self): + _default_action = self.getInt(self.DEFAULT_ACTION_KEY) + if _default_action == self.ACTION_ALLOW_IDX: + return self.ACTION_ALLOW + else: + return self.ACTION_DENY + + def setRulesDurationFilter(self, ignore_temporary_rules=False, temp_rules=1): + try: + if ignore_temporary_rules: + Config.RULES_DURATION_FILTER = [ + Config.DURATION_ONCE, Config.DURATION_30s, Config.DURATION_5m, + Config.DURATION_15m, Config.DURATION_30m, Config.DURATION_1h, + Config.DURATION_UNTIL_RESTART] + + Config.RULES_DURATION_FILTER = [ + rule for rule in Config.RULES_TEMPORARY_LIST + if Config.RULES_TEMPORARY_LIST.index(rule) < temp_rules + ] + Config.RULES_ACTIVE_TEMPORARY_RULES = [ + rule for rule in Config.RULES_TEMPORARY_LIST + if Config.RULES_TEMPORARY_LIST.index(rule) >= temp_rules + ] + #print("Temp rules preserved (RULES_DURATION_FILTER):", Config.RULES_DURATION_FILTER) + #print("Temp rules to delete (ACTIVE_TEMPORARY_RULES):", Config.RULES_ACTIVE_TEMPORARY_RULES) + + else: + Config.RULES_DURATION_FILTER = [] + except Exception as e: + print("setRulesDurationFilter() exception:", e) + + def getMaxMsgLength(self): + """return maximum configured length for the gRPC channel. + Default size is 4MB, but in some scenarios it's not enough. + """ + maxmsglen = 4194304 + maxmsglencfg = self.getSettings(Config.DEFAULT_SERVER_MAX_MESSAGE_LENGTH) + if maxmsglencfg == '4MiB': + maxmsglen = 4194304 + elif maxmsglencfg == '8MiB': + maxmsglen = 8388608 + elif maxmsglencfg == '16MiB': + maxmsglen = 16777216 + + print("gRPC Max Message Length:", maxmsglencfg) + print(" Bytes:", maxmsglen) + + return maxmsglen diff --git a/ui/opensnitch/customwidgets/__init__.py b/ui/opensnitch/customwidgets/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ui/opensnitch/customwidgets/addresstablemodel.py b/ui/opensnitch/customwidgets/addresstablemodel.py new file mode 100644 index 0000000..7873e5a --- /dev/null +++ b/ui/opensnitch/customwidgets/addresstablemodel.py @@ -0,0 +1,63 @@ + +from PyQt5.QtSql import QSqlQuery + +from opensnitch.utils import AsnDB +from opensnitch.customwidgets.generictableview import GenericTableModel +from PyQt5.QtCore import QCoreApplication as QC + +class AddressTableModel(GenericTableModel): + + def __init__(self, tableName, headerLabels): + super().__init__(tableName, headerLabels) + self.asndb = AsnDB.instance() + self.reconfigureColumns() + + def reconfigureColumns(self): + self.headerLabels = [] + self.setHorizontalHeaderLabels(self.headerLabels) + self.headerLabels.append(QC.translate("stats", "What", "")) + self.headerLabels.append(QC.translate("stats", "Hits", "")) + self.headerLabels.append(QC.translate("stats", "Network name", "")) + self.setHorizontalHeaderLabels(self.headerLabels) + self.setColumnCount(len(self.headerLabels)) + self.lastColumnCount = len(self.headerLabels) + + def setQuery(self, q, db): + self.origQueryStr = q + self.db = db + + if self.prevQueryStr != self.origQueryStr: + self.realQuery = QSqlQuery(q, db) + + self.realQuery.exec_() + self.realQuery.last() + + queryRows = max(0, self.realQuery.at()+1) + self.totalRowCount = queryRows + self.setRowCount(self.totalRowCount) + + queryColumns = self.realQuery.record().count() + if self.asndb.is_available() and queryColumns < 3: + self.reconfigureColumns() + else: + # update view's columns + if queryColumns != self.lastColumnCount: + self.setModelColumns(queryColumns) + + self.prevQueryStr = self.origQueryStr + self.rowCountChanged.emit() + + def fillVisibleRows(self, q, upperBound, force=False): + super().fillVisibleRows(q, upperBound, force) + + if self.asndb.is_available() == True and self.columnCount() <= 3: + for n, col in enumerate(self.items): + try: + if len(col) < 2: + continue + col[2] = self.asndb.get_asn(col[0]) + except: + col[2] = "" + finally: + self.items[n] = col + self.lastItems = self.items diff --git a/ui/opensnitch/customwidgets/colorizeddelegate.py b/ui/opensnitch/customwidgets/colorizeddelegate.py new file mode 100644 index 0000000..7b75046 --- /dev/null +++ b/ui/opensnitch/customwidgets/colorizeddelegate.py @@ -0,0 +1,84 @@ +from PyQt5 import Qt, QtCore +from PyQt5.QtWidgets import QApplication + +# PyQt5 >= v5.15.8 (28/01/2023) (#821) +if hasattr(Qt, 'QItemDelegate'): + from PyQt5.Qt import QItemDelegate, QStyleOptionViewItem +else: + from PyQt5.QtWidgets import QItemDelegate, QStyleOptionViewItem + +class ColorizedDelegate(QItemDelegate): + HMARGIN = 0 + VMARGIN = 1 + + def __init__(self, parent=None, *args, actions={}): + QItemDelegate.__init__(self, parent, *args) + self._actions = actions + self.modelColumns = parent.model().columnCount() + self._style = QApplication.style() + + def setConfig(self, actions): + self._actions = actions + + #@profile_each_line + def paint(self, painter, option, index): + """Override default widget style to personalize it with our own. + """ + if self._actions.get('actions') == None: + return super().paint(painter, option, index) + if not index.isValid(): + return super().paint(painter, option, index) + cellValue = index.data(QtCore.Qt.DisplayRole) + if cellValue == None: + return super().paint(painter, option, index) + + # initialize new QStyleOptionViewItem with the default options of this + # cell. + option = QStyleOptionViewItem(option) + + # by default use item's default attributes. + # if we modify any of them, set it to False + nocolor=True + + # don't call these functions in for-loops + cellRect = QtCore.QRect(option.rect) + curColumn = index.column() + curRow = index.row() + cellAlignment = option.displayAlignment + defaultPen = painter.pen() + defaultBrush = painter.brush() + + self._style = QApplication.style() + # get default margins in order to respect them. + # option.widget is the QTableView + hmargin = self._style.pixelMetric( + self._style.PM_FocusFrameHMargin, None, option.widget + ) + 1 + vmargin = self._style.pixelMetric( + self._style.PM_FocusFrameVMargin, None, option.widget + ) + 1 + + # set default margins for this cell + cellRect.adjust(hmargin, vmargin, -painter.pen().width(), -painter.pen().width()) + + for a in self._actions['actions']: + action = self._actions['actions'][a] + modified = action.run( + (painter, + option, + index, + self._style, + self.modelColumns, + curRow, + curColumn, + defaultPen, + defaultBrush, + cellAlignment, + cellRect, + cellValue) + ) + if modified[0]: + nocolor=False + + if nocolor: + super().paint(painter, option, index) diff --git a/ui/opensnitch/customwidgets/firewalltableview.py b/ui/opensnitch/customwidgets/firewalltableview.py new file mode 100644 index 0000000..5acdba5 --- /dev/null +++ b/ui/opensnitch/customwidgets/firewalltableview.py @@ -0,0 +1,326 @@ + +from PyQt5 import QtCore +from PyQt5.QtGui import QStandardItemModel, QStandardItem +from PyQt5.QtSql import QSqlQuery, QSqlError +from PyQt5.QtWidgets import QTableView, QAbstractSlider, QItemDelegate, QAbstractItemView, QPushButton, QWidget, QVBoxLayout +from PyQt5.QtCore import pyqtSignal +from PyQt5.QtCore import QCoreApplication as QC + +from opensnitch.nodes import Nodes +from opensnitch.firewall import Firewall +from opensnitch.customwidgets.updownbtndelegate import UpDownButtonDelegate + +class FirewallTableModel(QStandardItemModel): + rowCountChanged = pyqtSignal() + columnCountChanged = pyqtSignal(int) + rowsUpdated = pyqtSignal(int, tuple) + rowsReordered = pyqtSignal(int, str, str, int, int) # filter, addr, key, old_pos, new_pos + + tableName = "" + # total row count which must de displayed in the view + totalRowCount = 0 + # last column count to compare against with + lastColumnCount = 0 + + FILTER_ALL = 0 + FILTER_BY_NODE = 1 + FILTER_BY_TABLE = 2 + FILTER_BY_CHAIN = 3 + FILTER_BY_QUERY = 4 + activeFilter = FILTER_ALL + + UP_BTN = -1 + DOWN_BTN = 1 + + COL_BTNS = 0 + COL_UUID = 1 + COL_ADDR = 2 + COL_CHAIN_NAME = 3 + COL_CHAIN_TABLE = 4 + COL_CHAIN_FAMILY = 5 + COL_CHAIN_HOOK = 6 + COL_ENABLED = 7 + COL_DESCRIPTION = 8 + COL_PARMS = 9 + COL_ACTION = 10 + COL_ACTION_PARMS = 11 + + headersAll = [ + "", # buttons + "", # uuid + QC.translate("firewall", "Node", ""), + QC.translate("firewall", "Name", ""), + QC.translate("firewall", "Table", ""), + QC.translate("firewall", "Family", ""), + QC.translate("firewall", "Hook", ""), + QC.translate("firewall", "Enabled", ""), + QC.translate("firewall", "Description", ""), + QC.translate("firewall", "Parameters", ""), + QC.translate("firewall", "Action", ""), + QC.translate("firewall", "ActionParms", ""), + ] + + items = [] + lastRules = [] + position = 0 + + def __init__(self, tableName): + self.tableName = tableName + self._nodes = Nodes.instance() + self._fw = Firewall.instance() + self.lastColumnCount = len(self.headersAll) + self.lastQueryArgs = () + + QStandardItemModel.__init__(self, 0, self.lastColumnCount) + self.setHorizontalHeaderLabels(self.headersAll) + + def filterByNode(self, addr): + self.activeFilter = self.FILTER_BY_NODE + self.fillVisibleRows(0, True, addr) + + def filterAll(self): + self.activeFilter = self.FILTER_ALL + self.fillVisibleRows(0, True) + + def filterByTable(self, addr, name, family): + self.activeFilter = self.FILTER_BY_TABLE + self.fillVisibleRows(0, True, addr, name, family) + + def filterByChain(self, addr, table, family, chain, hook): + self.activeFilter = self.FILTER_BY_CHAIN + self.fillVisibleRows(0, True, addr, table, family, chain, hook) + + def filterByQuery(self, query): + self.activeFilter = self.FILTER_BY_QUERY + self.fillVisibleRows(0, True, query) + + def reorderRows(self, action, row): + if (row.row()+action == self.rowCount() and action == self.DOWN_BTN) or \ + (row.row() == 0 and action == self.UP_BTN): + return + + # XXX: better use moveRow()? + newRow = [] + # save the row we're about to overwrite + for c in range(self.columnCount()): + item = self.index(row.row()+action, c) + itemText = item.data() + newRow.append(itemText) + # overwrite next item with current data + for c in range(self.columnCount()): + curItem = self.index(row.row(), c).data() + nextIdx = self.index(row.row()+action, c) + self.setData(nextIdx, curItem, QtCore.Qt.DisplayRole) + # restore row with the overwritten data + for i, nr in enumerate(newRow): + idx = self.index(row.row(), i) + self.setData(idx, nr, QtCore.Qt.DisplayRole) + + self.rowsReordered.emit( + self.activeFilter, + self.index(row.row()+action, self.COL_ADDR).data(), # address + self.index(row.row()+action, self.COL_UUID).data(), # key + row.row(), + row.row()+action) + + def refresh(self, force=False): + self.fillVisibleRows(0, force, *self.lastQueryArgs) + + #Some QSqlQueryModel methods must be mimiced so that this class can serve as a drop-in replacement + #mimic QSqlQueryModel.query() + def query(self): + return self + + #mimic QSqlQueryModel.query().lastError() + def lastError(self): + return QSqlError() + + #mimic QSqlQueryModel.clear() + def clear(self): + self.items = [] + self.removeColumns(0, self.lastColumnCount) + self.setColumnCount(0) + self.setRowCount(0) + + # set columns based on query's fields + def setModelColumns(self, headers): + count = len(headers) + self.clear() + self.setHorizontalHeaderLabels(headers) + self.lastColumnCount = count + self.setColumnCount(self.lastColumnCount) + self.columnCountChanged.emit(count) + + def query(self): + return QSqlQuery() + + def setQuery(self, q, db, args=None): + self.refresh() + + def nextRecord(self, offset): + self.position += 1 + + def prevRecord(self, offset): + self.position -= 1 + + def fillVisibleRows(self, upperBound, force, *data): + if self.activeFilter == self.FILTER_BY_NODE and len(data) == 0: + return + + cols = [] + rules = [] + #don't trigger setItem's signals for each cell, instead emit dataChanged for all cells + self.blockSignals(True) + # mandatory for rows refreshing + self.layoutAboutToBeChanged.emit() + + if self.activeFilter == self.FILTER_BY_NODE: + rules = self._fw.get_node_rules(data[0]) + self.setModelColumns(self.headersAll) + elif self.activeFilter == self.FILTER_BY_TABLE: + rules = self._fw.filter_by_table(data[0], data[1], data[2]) + self.setModelColumns(self.headersAll) + elif self.activeFilter == self.FILTER_BY_CHAIN: + rules = self._fw.filter_by_chain(data[0], data[1], data[2], data[3], data[4]) + self.setModelColumns(self.headersAll) + elif self.activeFilter == self.FILTER_BY_QUERY: + rules = self._fw.filter_rules(data[0]) + self.setModelColumns(self.headersAll) + else: + self.setModelColumns(self.headersAll) + rules = self._fw.get_rules() + + self.addRows(rules) + + self.blockSignals(False) + if self.lastRules != rules or force == True: + self.layoutChanged.emit() + self.totalRowCount = len(rules) + self.setRowCount(self.totalRowCount) + self.rowsUpdated.emit(self.activeFilter, data) + self.dataChanged.emit(self.createIndex(0,0), self.createIndex(self.rowCount(), self.columnCount())) + + self.lastRules = rules + self.lastQueryArgs = data + del cols + del rules + + def addRows(self, rules): + self.items = [] + for rows in rules: + cols = [] + cols.append(QStandardItem("")) # buttons column + for cl in rows: + item = QStandardItem(cl) + item.setData(cl, QtCore.Qt.UserRole+1) + cols.append(item) + self.appendRow(cols) + + def dumpRows(self): + for rule in self.lastRules: + print(rule) + +class FirewallTableView(QTableView): + # how many rows can potentially be displayed in viewport + # the actual number of rows currently displayed may be less than this + maxRowsInViewport = 0 + rowsReordered = pyqtSignal(str) # addr + + def __init__(self, parent): + QTableView.__init__(self, parent) + self._fw = Firewall.instance() + self._fw.rules.rulesUpdated.connect(self._cb_fw_rules_updated) + + self.verticalHeader().setVisible(True) + self.horizontalHeader().setDefaultAlignment(QtCore.Qt.AlignCenter) + self.horizontalHeader().setStretchLastSection(True) + + # FIXME: if the firewall being used is iptables, hide the column to + # reorder rules, it's not supported. + updownBtn = UpDownButtonDelegate(self) + self.setItemDelegateForColumn(0, updownBtn) + updownBtn.clicked.connect(self._cb_fw_rule_position_changed) + + def _cb_fw_rules_updated(self): + self.model().refresh(True) + + def _cb_column_count_changed(self, num): + for i in range(num): + self.resizeColumnToContents(i) + + def _cb_fw_rule_position_changed(self, action, row): + self.model().reorderRows(action, row) + + def _cb_rows_reordered(self, view, node_addr, uuid, old_pos, new_pos): + if self._fw.swap_rules(view, node_addr, uuid, old_pos, new_pos): + self.rowsReordered.emit(node_addr) + + #@QtCore.pyqtSlot(int, tuple) + def _cb_rows_updated(self, view, data): + for c in range(self.model().rowCount()): + self.setColumnHidden(c, False) + #self.horizontalHeader().setSectionResizeMode( + # c, QHeaderView.ResizeToContents + #) + + self.setColumnHidden(FirewallTableModel.COL_BTNS, True) + self.setColumnHidden(FirewallTableModel.COL_UUID, True) + if view >= self.model().FILTER_BY_NODE: + # hide address column + self.setColumnHidden(FirewallTableModel.COL_ADDR, True) + if view >= self.model().FILTER_BY_TABLE: + self.setColumnHidden(FirewallTableModel.COL_CHAIN_TABLE, True) + self.setColumnHidden(FirewallTableModel.COL_CHAIN_FAMILY, True) + if view >= self.model().FILTER_BY_CHAIN: + # hide chain's name, family and hook + self.setColumnHidden(FirewallTableModel.COL_CHAIN_NAME, True) + self.setColumnHidden(FirewallTableModel.COL_CHAIN_HOOK, True) + self.setColumnHidden(FirewallTableModel.COL_BTNS, False) + + def filterAll(self): + self.model().filterAll() + + def filterByNode(self, addr): + self.model().filterByNode(addr) + + def filterByTable(self, addr, name, family): + self.model().filterByTable(addr, name, family) + + def filterByChain(self, addr, table, family, chain, hook): + self.model().filterByChain(addr, table, family, chain, hook) + + def filterByQuery(self, query): + self.model().filterByQuery(query) + + def refresh(self): + self.model().refresh(True) + + def clearSelection(self): + pass + + def copySelection(self): + selection = self.selectedIndexes() + if not selection: + return None + rows = [] + row = [] + lastRow = 0 + for idx in selection: + if idx.row() == lastRow: + row.append(self.model().index(idx.row(), idx.column()).data()) + else: + row = [] + lastRow = idx.row() + rows.append(row) + return rows + + def setModel(self, model): + super().setModel(model) + self.horizontalHeader().sortIndicatorChanged.disconnect() + self.setSortingEnabled(True) + self.model().columnCountChanged.connect(self._cb_column_count_changed) + model.rowsUpdated.connect(self._cb_rows_updated) + model.rowsReordered.connect(self._cb_rows_reordered) + + def setTrackingColumn(self, col): + pass diff --git a/ui/opensnitch/customwidgets/generictableview.py b/ui/opensnitch/customwidgets/generictableview.py new file mode 100644 index 0000000..fb6482b --- /dev/null +++ b/ui/opensnitch/customwidgets/generictableview.py @@ -0,0 +1,461 @@ +from PyQt5.QtGui import QColor, QStandardItemModel, QStandardItem +from PyQt5.QtSql import QSqlQueryModel, QSqlQuery, QSql +from PyQt5.QtWidgets import QTableView, QAbstractSlider +from PyQt5.QtCore import QItemSelectionModel, pyqtSignal, QEvent, Qt +import time +import math + +from PyQt5.QtCore import QCoreApplication as QC + +class GenericTableModel(QStandardItemModel): + rowCountChanged = pyqtSignal() + beginViewPortRefresh = pyqtSignal() + endViewPortRefresh = pyqtSignal() + + db = None + tableName = "" + # total row count which must de displayed in the view + totalRowCount = 0 + # + lastColumnCount = 0 + + # original query string before we modify it + origQueryStr = QSqlQuery() + # previous original query string; used to check if the query has changed + prevQueryStr = '' + # modified query object + realQuery = QSqlQuery() + + items = [] + lastItems = [] + + def __init__(self, tableName, headerLabels): + self.tableName = tableName + self.headerLabels = headerLabels + self.lastColumnCount = len(self.headerLabels) + QStandardItemModel.__init__(self, 0, self.lastColumnCount) + self.setHorizontalHeaderLabels(self.headerLabels) + + #Some QSqlQueryModel methods must be mimiced so that this class can serve as a drop-in replacement + #mimic QSqlQueryModel.query() + def query(self): + return self + + #mimic QSqlQueryModel.query().lastQuery() + def lastQuery(self): + return self.origQueryStr + + #mimic QSqlQueryModel.query().lastError() + def lastError(self): + return self.realQuery.lastError() + + #mimic QSqlQueryModel.clear() + def clear(self): + pass + + def rowCount(self, index=None): + """ensures that only the needed rows is created""" + return len(self.items) + + def data(self, index, role=Qt.DisplayRole): + """Paint rows with the data stored in self.items + """ + if role == Qt.DisplayRole or role == Qt.EditRole: + items_count = len(self.items) + if index.isValid() and items_count > 0 and index.row() < items_count: + return self.items[index.row()][index.column()] + return QStandardItemModel.data(self, index, role) + + # set columns based on query's fields + def setModelColumns(self, newColumns): + # Avoid firing signals while reconfiguring the view, it causes + # segfaults. + self.blockSignals(True); + + self.headerLabels = [] + self.removeColumns(0, self.lastColumnCount) + self.setHorizontalHeaderLabels(self.headerLabels) + for col in range(0, newColumns): + self.headerLabels.append(self.realQuery.record().fieldName(col)) + self.lastColumnCount = newColumns + self.setHorizontalHeaderLabels(self.headerLabels) + self.setColumnCount(len(self.headerLabels)) + + self.blockSignals(False); + + def setQuery(self, q, db): + self.origQueryStr = q + self.db = db + #print("q:", q) + + if self.prevQueryStr != self.origQueryStr: + self.realQuery = QSqlQuery(q, db) + + self.realQuery.exec_() + self.realQuery.last() + + queryRows = max(0, self.realQuery.at()+1) + self.totalRowCount = queryRows + self.setRowCount(self.totalRowCount) + + # update view's columns + queryColumns = self.realQuery.record().count() + if queryColumns != self.lastColumnCount: + self.setModelColumns(queryColumns) + + self.prevQueryStr = self.origQueryStr + self.rowCountChanged.emit() + + def nextRecord(self, offset): + cur_pos = self.realQuery.at() + q.seek(max(cur_pos, cur_pos+offset)) + + def prevRecord(self, offset): + cur_pos = self.realQuery.at() + q.seek(min(cur_pos, cur_pos-offset)) + + def refreshViewport(self, scrollValue, maxRowsInViewport, force=False): + """Refresh the viewport with data from the db. + Before making any changes, emit a signal which will perform several operations + (save current selected row, etc). + force var will force a refresh if the scrollbar is at the top or bottom of the + viewport, otherwise skip it to allow rows analyzing without refreshing. + """ + if not force: + return + + self.beginViewPortRefresh.emit() + # set records position to last, in order to get correctly the number of + # rows. + self.realQuery.last() + rowsFound = max(0, self.realQuery.at()+1) + if scrollValue == 0 or self.realQuery.at() == QSql.BeforeFirstRow: + self.realQuery.seek(QSql.BeforeFirstRow) + elif self.realQuery.at() == QSql.AfterLastRow: + self.realQuery.seek(rowsFound - maxRowsInViewport) + else: + self.realQuery.seek(min(scrollValue-1, self.realQuery.at())) + + upperBound = min(maxRowsInViewport, rowsFound) + self.setRowCount(self.totalRowCount) + + # only visible rows will be filled with data, and only if we're not + # updating the viewport already. + if force and (upperBound > 0 or self.realQuery.at() < 0): + self.fillVisibleRows(self.realQuery, upperBound, force) + self.endViewPortRefresh.emit() + + def fillVisibleRows(self, q, upperBound, force=False): + rowsLabels = [] + self.setVerticalHeaderLabels(rowsLabels) + + self.items = [] + cols = [] + #don't trigger setItem's signals for each cell, instead emit dataChanged for all cells + for x in range(0, upperBound): + q.next() + if q.at() < 0: + # if we don't set query to a valid record here, it gets stucked + # forever at -2/-1. + q.seek(upperBound) + break + rowsLabels.append(str(q.at()+1)) + cols = [] + for col in range(0, len(self.headerLabels)): + cols.append(str(q.value(col))) + + self.items.append(cols) + + self.setVerticalHeaderLabels(rowsLabels) + if self.lastItems != self.items or force == True: + self.dataChanged.emit(self.createIndex(0,0), self.createIndex(upperBound, len(self.headerLabels))) + self.lastItems = self.items + del cols + + def dumpRows(self): + rows = [] + q = QSqlQuery(self.db) + q.exec(self.origQueryStr) + q.seek(QSql.BeforeFirstRow) + while True: + q.next() + if q.at() == QSql.AfterLastRow: + break + row = [] + for col in range(0, len(self.headerLabels)): + row.append(q.value(col)) + rows.append(row) + return rows + + def copySelectedRows(self, start=QSql.BeforeFirstRow, end=QSql.AfterLastRow): + rows = [] + lastAt = self.realQuery.at() + self.realQuery.seek(start) + while True: + self.realQuery.next() + if self.realQuery.at() == QSql.AfterLastRow or len(rows) >= end: + break + row = [] + for col in range(0, len(self.headerLabels)): + row.append(self.realQuery.value(col)) + rows.append(row) + self.realQuery.seek(lastAt) + return rows + +class GenericTableView(QTableView): + # how many rows can potentially be displayed in viewport + # the actual number of rows currently displayed may be less than this + maxRowsInViewport = 0 + vScrollBar = None + curSelection = None + trackingCol = 0 + + def __init__(self, parent): + QTableView.__init__(self, parent) + self.mousePressed = False + + #eventFilter to catch key up/down events and wheel events + self.verticalHeader().setVisible(True) + self.horizontalHeader().setDefaultAlignment(Qt.AlignCenter) + self.horizontalHeader().setStretchLastSection(True) + #the built-in vertical scrollBar of this view is always off + self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + self.installEventFilter(self) + + def setVerticalScrollBar(self, vScrollBar): + self.vScrollBar = vScrollBar + self.vScrollBar.valueChanged.connect(self.onScrollbarValueChanged) + self.vScrollBar.setVisible(False) + + def setModel(self, model): + super().setModel(model) + model.rowCountChanged.connect(self.onRowCountChanged) + model.beginViewPortRefresh.connect(self.onBeginViewportRefresh) + model.endViewPortRefresh.connect(self.onEndViewportRefresh) + self.horizontalHeader().sortIndicatorChanged.disconnect() + self.setSortingEnabled(False) + + def setTrackingColumn(self, col): + """column used to track a selected row while scrolling""" + self.trackingCol = col + + def clear(self): + pass + + def refresh(self): + self.calculateRowsInViewport() + self.model().setRowCount(min(self.maxRowsInViewport, self.model().totalRowCount)) + self.model().refreshViewport(self.vScrollBar.value(), self.maxRowsInViewport, force=True) + + def forceViewRefresh(self): + return (self.vScrollBar.minimum() == self.vScrollBar.value() or self.vScrollBar.maximum() == self.vScrollBar.value()) + + def calculateRowsInViewport(self): + rowHeight = self.verticalHeader().defaultSectionSize() + #columnSize = self.horizontalHeader().defaultSectionSize() + # we don't want partial-height rows in viewport, hence .floor() + self.maxRowsInViewport = math.floor(self.viewport().height() / rowHeight)+1 + + def currentChanged(self, cur, prev): + #super().currentChanged(cur, prev) + if not self.mousePressed or prev.row() == cur.row(): + return + maxVal = self.maxRowsInViewport-1 + if cur.row() >= maxVal or prev.row() >= maxVal: + self.vScrollBar.setValue(self.vScrollBar.value() + 1) + elif cur.row() == 0: + self.vScrollBar.setValue(max(0, self.vScrollBar.value() - 1)) + + def mouseReleaseEvent(self, event): + super().mouseReleaseEvent(event) + self.mousePressed = False + + # save the selected index, to preserve selection when moving around. + def mousePressEvent(self, event): + # we need to call upper class to paint selections properly + super().mousePressEvent(event) + if event.button() != Qt.LeftButton: + return + self.mousePressed = True + + item = self.indexAt(event.pos()) + clickedItem = self.model().index(item.row(), self.trackingCol) + if clickedItem.data() == None: + return + + if item == None and self.curSelection == None: + return + elif item != None and self.curSelection == None: + # force selecting the row below + self.curSelection = "" + if clickedItem == None: + return + + if clickedItem.data() == self.curSelection: + self.curSelection = None + flags = QItemSelectionModel.Rows | QItemSelectionModel.Deselect + else: + self.curSelection = clickedItem.data() + flags = QItemSelectionModel.Rows | QItemSelectionModel.SelectCurrent + + self.selectionModel().setCurrentIndex( + clickedItem, + flags + ) + + def onBeginViewportRefresh(self): + # if the selected row due to scrolling up/down doesn't match with the + # saved index, deselect the row, because the saved index is out of the + # view. + index = self.selectionModel().selectedRows(self.trackingCol) + if len(index) == 0: + return + if index[0].data() != self.curSelection: + self.selectionModel().clear() + + def onEndViewportRefresh(self): + self._selectSavedIndex() + + def resizeEvent(self, event): + super().resizeEvent(event) + #refresh the viewport data based on new geometry + self.refresh() + + def onRowCountChanged(self): + totalCount = self.model().totalRowCount + self.vScrollBar.setVisible(True if totalCount > self.maxRowsInViewport else False) + + self.vScrollBar.setMinimum(0) + # we need to substract the displayed rows to the total rows, to scroll + # down correctly. + self.vScrollBar.setMaximum(max(0, totalCount - self.maxRowsInViewport+1)) + + self.model().refreshViewport(self.vScrollBar.value(), self.maxRowsInViewport, force=self.forceViewRefresh()) + + def clearSelection(self): + self.selectionModel().reset() + self.selectionModel().clearCurrentIndex() + + def copySelection(self): + model = self.selectionModel() + curModel = self.model() + selection = model.selectedRows() + if not selection: + return None + + rows = [] + for idx in selection: + row = [] + for col in range(0, curModel.columnCount()): + row.append(curModel.index(idx.row(), col).data()) + rows.append(row) + return rows + + def getCurrentIndex(self): + return self.selectionModel().currentIndex().internalId() + + def currentSelection(self): + return self.curSelection + + def selectItem(self, _data, _column): + """Select a row based on the data displayed on the given column. + """ + items = self.model().findItems(_data, column=_column) + if len(items) > 0: + self.selectionModel().setCurrentIndex( + items[0].index(), + QItemSelectionModel.Rows | QItemSelectionModel.SelectCurrent + ) + + def _selectSavedIndex(self): + if self.curSelection == None or self.mousePressed: + return + + items = self.model().findItems(self.curSelection, column=self.trackingCol) + if len(items) > 0: + self.selectionModel().setCurrentIndex( + items[0].index(), + QItemSelectionModel.Rows | QItemSelectionModel.SelectCurrent + ) + + def _selectLastRow(self): + if self.curSelection != None: + return + internalId = self.getCurrentIndex() + self.selectionModel().setCurrentIndex( + self.model().createIndex(self.maxRowsInViewport-2, self.trackingCol, internalId), + QItemSelectionModel.Rows | QItemSelectionModel.SelectCurrent + ) + + def _selectRow(self, pos): + internalId = self.getCurrentIndex() + self.selectionModel().setCurrentIndex( + self.model().createIndex(pos, self.trackingCol, internalId), + QItemSelectionModel.Rows | QItemSelectionModel.SelectCurrent + ) + + def onScrollbarValueChanged(self, vSBNewValue): + self.model().refreshViewport(vSBNewValue, self.maxRowsInViewport, force=True) + + def onKeyUp(self): + self.curSelection = self.selectionModel().currentIndex().data() + if self.selectionModel().currentIndex().row() == 0: + self.vScrollBar.setValue(max(0, self.vScrollBar.value() - 1)) + + def onKeyDown(self): + self.curSelection = self.selectionModel().currentIndex().data() + if self.curSelection == None: + self._selectLastRow() + return + + curRow = self.selectionModel().currentIndex().row() + if curRow >= self.maxRowsInViewport-2: + self.onKeyPageDown() + self._selectRow(0) + else: + self._selectRow(curRow) + + def onKeyHome(self): + self.vScrollBar.setValue(0) + self.selectionModel().clear() + + def onKeyEnd(self): + self.vScrollBar.setValue(self.vScrollBar.maximum()) + self.selectionModel().clear() + self._selectLastRow() + + def onKeyPageUp(self): + newValue = max(0, self.vScrollBar.value() - self.maxRowsInViewport) + self.vScrollBar.setValue(newValue) + + def onKeyPageDown(self): + if self.vScrollBar.isVisible() == False: + return + + newValue = self.vScrollBar.value() + (self.maxRowsInViewport-2) + self.vScrollBar.setValue(newValue) + + def eventFilter(self, obj, event): + if event.type() == QEvent.KeyPress: + # FIXME: setValue() does not update the scrollbars correctly in + # some pyqt versions. + if event.key() == Qt.Key_Up: + self.onKeyUp() + elif event.key() == Qt.Key_Down: + self.onKeyDown() + elif event.key() == Qt.Key_Home: + self.onKeyHome() + elif event.key() == Qt.Key_End: + self.onKeyEnd() + elif event.key() == Qt.Key_PageUp: + self.onKeyPageUp() + elif event.key() == Qt.Key_PageDown: + self.onKeyPageDown() + elif event.key() == Qt.Key_Escape: + self.selectionModel().clear() + self.curSelection = None + elif event.type() == QEvent.Wheel: + self.vScrollBar.wheelEvent(event) + return True + + return super(GenericTableView, self).eventFilter(obj, event) diff --git a/ui/opensnitch/customwidgets/main.py b/ui/opensnitch/customwidgets/main.py new file mode 100644 index 0000000..cb86ab0 --- /dev/null +++ b/ui/opensnitch/customwidgets/main.py @@ -0,0 +1,498 @@ +from PyQt5 import QtCore +from PyQt5.QtGui import QColor, QStandardItemModel, QStandardItem +from PyQt5.QtSql import QSqlQueryModel, QSqlQuery, QSql +from PyQt5.QtWidgets import QTableView +from PyQt5.QtCore import QItemSelectionModel, pyqtSignal, QEvent +import time +import math + +from PyQt5.QtCore import QCoreApplication as QC +class ColorizedQSqlQueryModel(QSqlQueryModel): + """ + model=CustomQSqlQueryModel( + modelData= + { + 'colorize': + {'offline': (QColor(QtCore.Qt.red), 2)}, + 'alignment': { Qt.AlignLeft, 2 } + } + ) + """ + RED = QColor(QtCore.Qt.red) + GREEN = QColor(QtCore.Qt.green) + + def __init__(self, modelData={}): + QSqlQueryModel.__init__(self) + self._model_data = modelData + + def data(self, index, role=QtCore.Qt.DisplayRole): + if not index.isValid(): + return QSqlQueryModel.data(self, index, role) + + column = index.column() + row = index.row() + + if role == QtCore.Qt.TextAlignmentRole: + return QtCore.Qt.AlignCenter + if role == QtCore.Qt.TextColorRole: + for _, what in enumerate(self._model_data): + d = QSqlQueryModel.data(self, self.index(row, self._model_data[what][1]), QtCore.Qt.DisplayRole) + if column == self._model_data[what][1] and what in d: + return self._model_data[what][0] + + return QSqlQueryModel.data(self, index, role) + +class ConnectionsTableModel(QStandardItemModel): + rowCountChanged = pyqtSignal() + + #max rowid in the db; starts with 1, not with 0 + maxRowId = 0 + #previous total number of rows in the db when the filter was applied + prevFiltRowCount = 0 + #total number of rows in the db when the filter was not applied + prevNormRowCount = 0 + #total row count which must de displayed in the view + totalRowCount = 0 + #new rows which must be added to the top of the rows displayed in the view + prependedRowCount = 0 + + db = None + #original query string before we modify it + origQueryStr = QSqlQuery() + #modified query object + realQuery = QSqlQuery() + #previous original query string; used to check if the query has changed + prevQueryStr = '' + #whether or not the original query has a filter (a WHERE condition) + isQueryFilter = False + limit = None + + #a map for fast lookup or rows when filter is enabled + #contains ranges of rowids and count of filter hits + #range format {'from': <rowid>, 'to': <rowid>, 'hits':<int>} + #including the 'from' rowid up to but NOT including the 'to' rowid + map = [] + rangeSize = 1000 + #all unique/distinct values for each column will be stored here + distinct = {'time':[], 'process':[], 'dst_host':[], 'dst_ip':[], 'dst_port':[], 'rule':[], 'node':[], 'protocol':[]} + #what was the last rowid\time when the distinct value were updates + distinctLastRowId = 0 + distinctLastUpdateTime = time.time() + + def __init__(self): + self.headerLabels = [ + QC.translate("stats", "Time", "This is a word, without spaces and symbols.").replace(" ", ""), + QC.translate("stats", "Node", "This is a word, without spaces and symbols.").replace(" ", ""), + QC.translate("stats", "Action", "This is a word, without spaces and symbols.").replace(" ", ""), + QC.translate("stats", "Destination", "This is a word, without spaces and symbols.").replace(" ", ""), + QC.translate("stats", "Protocol", "This is a word, without spaces and symbols.").replace(" ", ""), + QC.translate("stats", "Process", "This is a word, without spaces and symbols.").replace(" ", ""), + QC.translate("stats", "Rule", "This is a word, without spaces and symbols.").replace(" ", ""), + ] + QStandardItemModel.__init__(self, 0, len(self.headerLabels)) + self.setHorizontalHeaderLabels(self.headerLabels) + + #Some QSqlQueryModel methods must be mimiced so that this class can serve as a drop-in replacement + #mimic QSqlQueryModel.query() + def query(self): + return self + + #mimic QSqlQueryModel.query().lastQuery() + def lastQuery(self): + return self.origQueryStr + + #mimic QSqlQueryModel.query().lastError() + def lastError(self): + return self.realQuery.lastError() + + #mimic QSqlQueryModel.clear() + def clear(self): + pass + + def setQuery(self, q, db): + self.origQueryStr = q + self.db = db + maxRowIdQuery = QSqlQuery(db) + maxRowIdQuery.setForwardOnly(True) + maxRowIdQuery.exec("SELECT MAX(rowid) FROM connections") + maxRowIdQuery.first() + value = maxRowIdQuery.value(0) + self.maxRowId = 0 if value == '' else int(value) + self.updateDistinctIfNeeded() + self.limit = int(q.split(' ')[-1]) if q.split(' ')[-2] == 'LIMIT' else None + self.isQueryFilter = True if ("LIKE '%" in q and "LIKE '% %'" not in q) or 'Action = "' in q else False + + self.realQuery = QSqlQuery(db) + isTotalRowCountChanged = False + isQueryChanged = False + if self.prevQueryStr != q: + isQueryChanged = True + if self.isQueryFilter: + if isQueryChanged: + self.buildMap() + largestRowIdInMap = self.map[0]['from'] + newRowsCount = self.maxRowId - largestRowIdInMap + self.prependedRowCount = 0 + + if newRowsCount > 0: + starttime = time.time() + self.realQuery.setForwardOnly(True) + for offset in range(0, newRowsCount, self.rangeSize): + lowerBound = largestRowIdInMap + offset + upperBound = min(lowerBound + self.rangeSize, self.maxRowId) + part1, part2 = q.split('ORDER') + qStr = part1 + 'AND rowid>'+ str(lowerBound) + ' AND rowid<=' + str(upperBound) + ' ORDER' + part2 + self.realQuery.exec(qStr) + self.realQuery.last() + rowsInRange = max(0, self.realQuery.at()+1) + if self.map[0]['from'] - self.map[0]['to'] < self.rangeSize: + #consolidate with the previous range; we don't want many small ranges + self.map[0]['from'] = upperBound + self.map[0]['hits'] += rowsInRange + else: + self.map.insert(0, {'from':upperBound, 'to':lowerBound, 'hits':rowsInRange}) + self.prependedRowCount += rowsInRange + if time.time() - starttime > 0.5: + #dont freeze the UI when fetching too many recent rows + break + + self.totalRowCount = 0 + for i in self.map: + self.totalRowCount += i['hits'] + if self.totalRowCount != self.prevFiltRowCount: + isTotalRowCountChanged = True + self.prevFiltRowCount = self.totalRowCount + else: #self.isQueryFilter == False + self.prependedRowCount = self.maxRowId - self.prevNormRowCount + self.totalRowCount = self.maxRowId + if self.totalRowCount != self.prevNormRowCount: + isTotalRowCountChanged = True + self.prevNormRowCount = self.totalRowCount + + self.prevQueryStr = self.origQueryStr + if isTotalRowCountChanged or self.prependedRowCount > 0 or isQueryChanged: + self.rowCountChanged.emit() + + #fill self.map with data + def buildMap(self): + self.map = [] + q = QSqlQuery(self.db) + q.setForwardOnly(True) + self.updateDistinctIfNeeded(True) + filterStr = self.getFilterStr() + actionStr = self.getActionStr() + #we only want to know the count of matching rows + qStr = "SELECT COUNT(*) from connections WHERE (rowid> :lowerBound AND rowid<= :upperBound)" + if actionStr: + qStr += ' AND ' + actionStr + matchStr = self.getMatch(filterStr) if filterStr else None + if matchStr: + qStr += ' AND ' + matchStr + qStr += ' LIMIT ' + str(self.limit) if self.limit else '' + q.prepare(qStr) + + totalRows = 0 + for offset in range(self.maxRowId, -1, -self.rangeSize): + upperBound = offset + lowerBound = max(0, upperBound - self.rangeSize) + if (not filterStr and actionStr) or (filterStr and matchStr): + #either 1) only action was present or 2) filter which has a match (with or without action) + q.bindValue(":lowerBound", str(lowerBound)) + q.bindValue(":upperBound", str(upperBound)) + q.exec_() + q.first() + rowsInRange = int(q.value(0)) + else: + rowsInRange = 0 + totalRows += rowsInRange + self.map.append({'from':upperBound, 'to':lowerBound, 'hits':rowsInRange}) + if self.limit and totalRows >= self.limit: + break + + #periodically keep track of all distinct values for each column + #this is needed in order to build efficient queries when the filter is applied + def updateDistinctIfNeeded(self, force=False): + if (not force and (time.time() - self.distinctLastUpdateTime) < 10) or self.maxRowId == self.distinctLastRowId: + return + if (self.maxRowId < self.distinctLastRowId): + #the db has been cleared, re-init the values + self.distinctLastRowId = 0 + self.distinct = {'time':[], 'process':[], 'dst_host':[], 'dst_ip':[], 'dst_port':[], 'rule':[], 'node':[], 'protocol':[]} + q = QSqlQuery(self.db) + q.setForwardOnly(True) + for column in self.distinct.keys(): + q.exec('SELECT DISTINCT ' + column + ' FROM connections WHERE rowid>' + + str(self.distinctLastRowId) + ' AND rowid<=' + str(self.maxRowId)) + while q.next(): + if q.value(0) not in self.distinct[column]: + self.distinct[column].append(q.value(0)) + self.distinctLastRowId =self.maxRowId + self.distinctLastUpdateTime = time.time() + + #refresh the viewport with data from the db + #"value" is vertical scrollbar's value + def refreshViewport(self, value, maxRowsInViewport): + q = QSqlQuery(self.db) + #sequential number of topmost/bottommost rows in viewport (numbering starts from the bottom with 1 not with 0) + botRowNo = max(1, self.totalRowCount - (value + maxRowsInViewport-1)) + topRowNo = min(botRowNo + maxRowsInViewport-1, self.totalRowCount) + + if not self.isQueryFilter: + part1, part2 = self.origQueryStr.split('ORDER') + qStr = part1 + 'WHERE rowid>='+ str(botRowNo) + ' AND rowid<=' + str(topRowNo) + ' ORDER' + part2 + else: + self.updateDistinctIfNeeded(True) + #replace query part between WHERE and ORDER + qStr = self.origQueryStr.split('WHERE')[0] + ' WHERE ' + actionStr = self.getActionStr() + if actionStr: + qStr += actionStr + " AND " + #find inside the map the range(s) in which top and bottom rows are located + total, offsetInRange, botRowFound, topRowFound = 0, None, False, False + ranges = [{'from':0, 'to':0, 'hits':0}] + for i in reversed(self.map): + if total + i['hits'] >= botRowNo: + botRowFound = True + if total + i['hits'] >= topRowNo: + topRowFound = True + if botRowFound and i['hits'] > 0: + if i['to'] == ranges[-1]['from']: + #merge two adjacent ranges + ranges[-1]['from'] = i['from'] + ranges[-1]['hits'] += i['hits'] + else: + ranges.append(i.copy()) + if topRowFound: + offsetInRange = i['hits'] - (topRowNo - total) + break + total += i['hits'] + + rangeStr = '' + if len(ranges) > 0: + rangeStr = '(' + for r in ranges: + rangeStr += '(rowid>' + str(r['to']) + ' AND rowid<=' + str(r['from']) + ') OR ' + rangeStr = rangeStr[:-3] #remove trailing 'OR ' + rangeStr += ') AND ' + qStr += rangeStr + + filterStr = self.getFilterStr() + matchStr = self.getMatch(filterStr) if filterStr else None + if matchStr: + qStr += matchStr + " AND " + qStr = qStr[:-4] #remove trailing ' AND' + qStr += ' ORDER '+ self.origQueryStr.split('ORDER')[1] + + q.exec(qStr) + q.last() + rowsFound = max(0, q.at()+1) + if not self.isQueryFilter: + q.seek(QSql.BeforeFirstRow) + else: + #position the db cursor on topRowNo + q.seek(QSql.BeforeFirstRow if offsetInRange == 0 else offsetInRange-1) + upperBound = min(maxRowsInViewport, rowsFound) + self.setRowCount(upperBound) + #only visible rows will be filled with data + if upperBound > 0: + #don't trigger setItem's signals for each cell, instead emit dataChanged for all cells + self.blockSignals(True) + for x in range(0, upperBound): + q.next() + for col in range(0, len(self.headerLabels)): + self.setItem(x, col, QStandardItem(q.value(col))) + self.blockSignals(False) + self.dataChanged.emit(self.createIndex(0,0), self.createIndex(upperBound, len(self.headerLabels))) + + #form a condition string for the query: if filterStr is (partially) present in any of the columns + def getMatch (self, filterStr): + match = {} + for column in self.distinct.keys(): + match[column] = [] + for value in self.distinct[column]: + if filterStr in value: + match[column].append(value) + matchStr = None + if any([match[col] for col in match]): + matchStr = '( ' + if match['time']: + matchStr += "time IN ('" + "','".join(match['time']) + "') OR" + if match['process']: + matchStr += "process IN ('" + "','".join(match['process']) + "') OR" + if match['dst_host']: + matchStr += " (dst_host != '' AND dst_host IN ('" + "','".join(match['dst_host']) + "') ) OR" + if match['dst_ip']: + matchStr += " (dst_host = '' AND dst_ip IN ('" + "','".join(match['dst_ip']) + "') ) OR" + if match['dst_port']: + matchStr += " dst_port IN ('" + "','".join(match['dst_port']) + "') OR" + if match['rule']: + matchStr += " rule IN ('" + "','".join(match['rule']) + "') OR" + if match['node']: + matchStr += " node IN ('" + "','".join(match['node']) + "') OR" + if match['protocol']: + matchStr += " protocol IN ('" + "','".join(match['protocol']) + "') OR" + matchStr = matchStr[:-2] #remove trailing 'OR' + matchStr += ' )' + return matchStr + + #extract the filter string if any + def getFilterStr(self): + filterStr = None + if "LIKE '%" in self.origQueryStr: + filterStr = self.origQueryStr.split("LIKE '%")[1].split("%")[0] + return filterStr + + #extract the action string if any + def getActionStr(self): + actionStr = None + if 'WHERE Action = "' in self.origQueryStr: + actionCond = self.origQueryStr.split('WHERE Action = "')[1].split('"')[0] + actionStr = "action = '"+actionCond+"'" + return actionStr + + def dumpRows(self): + rows = [] + q = QSqlQuery(self.db) + q.exec(self.origQueryStr) + q.seek(QSql.BeforeFirstRow) + while True: + q.next() + if q.at() == QSql.AfterLastRow: + break + row = [] + for col in range(0, len(self.headerLabels)): + row.append(q.value(col)) + rows.append(row) + return rows + +class ConnectionsTableView(QTableView): + # how many rows can potentially be displayed in viewport + # the actual number of rows currently displayed may be less than this + maxRowsInViewport = 0 + #vertical scroll bar + vScrollBar = None + + def __init__(self, parent): + QTableView.__init__(self, parent) + #eventFilter to catch key up/down events and wheel events + self.installEventFilter(self) + self.verticalHeader().setVisible(False) + self.horizontalHeader().setDefaultAlignment(QtCore.Qt.AlignCenter) + self.horizontalHeader().setStretchLastSection(True) + #the built-in vertical scrollBar of this view is always off + self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + + def setVerticalScrollBar(self, vScrollBar): + self.vScrollBar = vScrollBar + self.vScrollBar.valueChanged.connect(self.onValueChanged) + self.vScrollBar.setVisible(False) + + def setModel(self, model): + super().setModel(model) + model.rowCountChanged.connect(self.onRowCountChanged) + model.rowsInserted.connect(self.onRowsInsertedOrRemoved) + model.rowsRemoved.connect(self.onRowsInsertedOrRemoved) + self.horizontalHeader().sortIndicatorChanged.disconnect() + self.setSortingEnabled(False) + + #model().rowCount() is always <= self.maxRowsInViewport + #stretch the bottom row; we don't want partial-height rows at the bottom + #this will only trigger if rowCount value was changed + def onRowsInsertedOrRemoved(self, parent, start, end): + if self.model().rowCount() == self.maxRowsInViewport: + self.verticalHeader().setStretchLastSection(True) + else: + self.verticalHeader().setStretchLastSection(False) + + def resizeEvent(self, event): + super().resizeEvent(event) + #refresh the viewport data based on new geometry + self.calculateRowsInViewport() + self.model().setRowCount(min(self.maxRowsInViewport, self.model().totalRowCount)) + self.model().refreshViewport(self.vScrollBar.value(), self.maxRowsInViewport) + + def calculateRowsInViewport(self): + rowHeight = self.verticalHeader().defaultSectionSize() + #we don't want partial-height rows in viewport, hence .floor() + self.maxRowsInViewport = math.floor(self.viewport().height() / rowHeight) + + def onValueChanged(self, vSBNewValue): + savedIndex = self.selectionModel().currentIndex() + self.model().refreshViewport(vSBNewValue, self.maxRowsInViewport) + #restore selection which was removed by model's refreshing the data + self.selectionModel().setCurrentIndex(savedIndex, QItemSelectionModel.Rows | QItemSelectionModel.SelectCurrent) + + # if ( scrollbar at the top or row limit set): + # let new rows "push down" older rows without changing the scrollbar position + # else: + # don't update data in viewport, only change scrollbar position. + def onRowCountChanged(self): + totalCount = self.model().totalRowCount + scrollBar = self.vScrollBar + scrollBar.setVisible(True if totalCount > self.maxRowsInViewport else False) + scrollBarValue = scrollBar.value() + if self.model().limit: + newValue = min(scrollBarValue, self.model().limit - self.maxRowsInViewport) + scrollBar.setMinimum(0) + scrollBar.setMaximum( min(totalCount, self.model().limit) - self.maxRowsInViewport) + if scrollBarValue != newValue: + #setValue does not trigger valueChanged if new value is the same as old + scrollBar.setValue(newValue) + else: + scrollBar.valueChanged.emit(newValue) + else: + scrollBar.setMinimum(0) + scrollBar.setMaximum(max(0, totalCount - self.maxRowsInViewport)) + if scrollBarValue == 0: + scrollBar.valueChanged.emit(0) + elif scrollBarValue > 0: + if self.model().prependedRowCount == 0: + scrollBar.valueChanged.emit(scrollBarValue) + else: + scrollBar.setValue(scrollBarValue + self.model().prependedRowCount) + + def onKeyUp(self): + if self.selectionModel().currentIndex().row() == 0: + self.vScrollBar.setValue(self.vScrollBar.value() - 1) + + def onKeyDown(self): + if self.selectionModel().currentIndex().row() == self.maxRowsInViewport - 1: + self.vScrollBar.setValue(self.vScrollBar.value() + 1) + + def onKeyHome(self): + self.vScrollBar.setValue(0) + self.selectionModel().setCurrentIndex(self.model().createIndex(0, 0), QItemSelectionModel.Rows | QItemSelectionModel.SelectCurrent) + + def onKeyEnd(self): + self.vScrollBar.setValue(self.vScrollBar.maximum()) + self.selectionModel().setCurrentIndex(self.model().createIndex(min(self.maxRowsInViewport, self.model().totalRowCount) - 1, 0), QItemSelectionModel.Rows | QItemSelectionModel.SelectCurrent) + + def onKeyPageUp(self): + #scroll up only when on the first row + if self.selectionModel().currentIndex().row() != 0: + return + self.vScrollBar.setValue(self.vScrollBar.value() - self.maxRowsInViewport) + + def onKeyPageDown(self): + #scroll down only when on the last row + if self.selectionModel().currentIndex().row() != self.maxRowsInViewport - 1: + return + self.vScrollBar.setValue(self.vScrollBar.value() + self.maxRowsInViewport) + + def eventFilter(self, obj, event): + if event.type() == QEvent.KeyPress: + if event.key() == QtCore.Qt.Key_Up: + self.onKeyUp() + elif event.key() == QtCore.Qt.Key_Down: + self.onKeyDown() + elif event.key() == QtCore.Qt.Key_Home: + self.onKeyHome() + elif event.key() == QtCore.Qt.Key_End: + self.onKeyEnd() + elif event.key() == QtCore.Qt.Key_PageUp: + self.onKeyPageUp() + elif event.key() == QtCore.Qt.Key_PageDown: + self.onKeyPageDown() + elif event.type() == QEvent.Wheel: + self.vScrollBar.wheelEvent(event) + return False diff --git a/ui/opensnitch/customwidgets/updownbtndelegate.py b/ui/opensnitch/customwidgets/updownbtndelegate.py new file mode 100644 index 0000000..51a9300 --- /dev/null +++ b/ui/opensnitch/customwidgets/updownbtndelegate.py @@ -0,0 +1,53 @@ +from PyQt5 import Qt, QtCore +from PyQt5.QtGui import QRegion +from PyQt5.QtWidgets import QItemDelegate, QAbstractItemView, QPushButton, QWidget, QVBoxLayout, QSizePolicy +from PyQt5.QtCore import pyqtSignal + +class UpDownButtonDelegate(QItemDelegate): + clicked = pyqtSignal(int, QtCore.QModelIndex) + + UP=-1 + DOWN=1 + + def paint(self, painter, option, index): + if ( + isinstance(self.parent(), QAbstractItemView) + and self.parent().model() is index.model() + ): + self.parent().openPersistentEditor(index) + + def createEditor(self, parent, option, index): + w = QWidget(parent) + w.setContentsMargins(0, 0, 0, 0) + w.setAutoFillBackground(True) + + layout = QVBoxLayout(w) + layout.setContentsMargins(0, 0, 0, 0) + + btnUp = QPushButton(parent) + btnUp.setText("⇡") + btnUp.setFlat(True) + btnUp.clicked.connect(lambda: self._cb_button_clicked(self.UP, index)) + + btnDown = QPushButton(parent) + btnDown.setText("⇣") + btnDown.setFlat(True) + btnDown.clicked.connect(lambda: self._cb_button_clicked(self.DOWN, index)) + + layout.addWidget(btnUp) + layout.addWidget(btnDown) + return w + + def _cb_button_clicked(self, action, idx): + self.clicked.emit(action, idx) + + def updateEditorGeometry(self, editor, option, index): + rect = QtCore.QRect(option.rect) + minWidth = editor.minimumSizeHint().width() + if rect.width() < minWidth: + rect.setWidth(minWidth) + editor.setGeometry(rect) + # create a new mask based on the option rectangle, then apply it + mask = QRegion(0, 0, option.rect.width(), option.rect.height()) + editor.setProperty('offMask', mask) + editor.setMask(mask) diff --git a/ui/opensnitch/database/__init__.py b/ui/opensnitch/database/__init__.py new file mode 100644 index 0000000..a2a3507 --- /dev/null +++ b/ui/opensnitch/database/__init__.py @@ -0,0 +1,632 @@ +from PyQt5.QtSql import QSqlDatabase, QSqlQueryModel, QSqlQuery +import threading +import sys +import os +from datetime import datetime, timedelta + +class Database: + db = None + __instance = None + DB_IN_MEMORY = "file::memory:" + DB_TYPE_MEMORY = 0 + DB_TYPE_FILE = 1 + DB_JRNL_WAL = False + + # Sqlite3 journal modes + DB_JOURNAL_MODE_LIST = { + 0: "DELETE", + 1: "TRUNCATE", + 2: "PERSIST", + 3: "MEMORY", + 4: "WAL", + 5: "OFF", + } + + # increase accordingly whenever the schema is updated + DB_VERSION = 3 + + @staticmethod + def instance(): + if Database.__instance == None: + Database.__instance = Database() + return Database.__instance + + def __init__(self, dbname="db"): + self._lock = threading.RLock() + self.db = None + self.db_file = Database.DB_IN_MEMORY + self.db_jrnl_wal = Database.DB_JRNL_WAL + self.db_name = dbname + + def initialize(self, dbtype=DB_TYPE_MEMORY, dbfile=DB_IN_MEMORY, dbjrnl_wal=DB_JRNL_WAL, db_name="db"): + if dbtype != Database.DB_TYPE_MEMORY: + self.db_file = dbfile + self.db_jrnl_wal = dbjrnl_wal + else: + # Always disable under pure memory mode + self.db_jrnl_wal = False + + is_new_file = not os.path.isfile(self.db_file) + + self.db = QSqlDatabase.addDatabase("QSQLITE", self.db_name) + self.db.setDatabaseName(self.db_file) + if dbtype == Database.DB_TYPE_MEMORY: + self.db.setConnectOptions("QSQLITE_OPEN_URI;QSQLITE_ENABLE_SHARED_CACHE") + if not self.db.open(): + print("\n ** Error opening DB: SQLite driver not loaded. DB name: %s\n" % self.db_file) + print("\n Available drivers: ", QSqlDatabase.drivers()) + sys.exit(-1) + + db_status, db_error = self.is_db_ok() + if db_status is False: + print("db.initialize() error:", db_error) + return False, db_error + + + if is_new_file: + print("is new file, or IN MEMORY, setting initial schema version") + self.set_schema_version(self.DB_VERSION) + + self._create_tables() + self._upgrade_db_schema() + return True, None + + def close(self): + try: + if self.db.isOpen(): + self.db.removeDatabase(self.db_name) + self.db.close() + except Exception as e: + print("db.close() exception:", e) + + def is_db_ok(self): + # XXX: quick_check may not be fast enough with some DBs on slow + # hardware. + q = QSqlQuery("PRAGMA quick_check;", self.db) + if q.exec_() is not True: + print(q.lastError().driverText()) + return False, q.lastError().driverText() + + if q.next() and q.value(0) != "ok": + return False, "Database is corrupted (1)" + + return True, None + + def get_db(self): + return self.db + + def get_db_file(self): + return self.db_file + + def get_new_qsql_model(self): + return QSqlQueryModel() + + def get_db_name(self): + return self.db_name + + def _create_tables(self): + # https://www.sqlite.org/wal.html + if self.db_file == Database.DB_IN_MEMORY: + self.set_schema_version(self.DB_VERSION) + # Disable journal (default) + self.set_journal_mode(5) + elif self.db_jrnl_wal is True: + # Set WAL mode (file+memory) + self.set_journal_mode(4) + else: + # Set DELETE mode (file) + self.set_journal_mode(0) + q = QSqlQuery("create table if not exists connections (" \ + "time text, " \ + "node text, " \ + "action text, " \ + "protocol text, " \ + "src_ip text, " \ + "src_port text, " \ + "dst_ip text, " \ + "dst_host text, " \ + "dst_port text, " \ + "uid text, " \ + "pid text, " \ + "process text, " \ + "process_args text, " \ + "process_cwd text, " \ + "rule text, " \ + "UNIQUE(node, action, protocol, src_ip, src_port, dst_ip, dst_port, uid, pid, process, process_args))", + self.db) + q = QSqlQuery("create index time_index on connections (time)", self.db) + q.exec_() + q = QSqlQuery("create index action_index on connections (action)", self.db) + q.exec_() + q = QSqlQuery("create index protocol_index on connections (protocol)", self.db) + q.exec_() + q = QSqlQuery("create index dst_host_index on connections (dst_host)", self.db) + q.exec_() + q = QSqlQuery("create index process_index on connections (process)", self.db) + q.exec_() + q = QSqlQuery("create index dst_ip_index on connections (dst_ip)", self.db) + q.exec_() + q = QSqlQuery("create index dst_port_index on connections (dst_port)", self.db) + q.exec_() + q = QSqlQuery("create index rule_index on connections (rule)", self.db) + q.exec_() + q = QSqlQuery("create index node_index on connections (node)", self.db) + q.exec_() + q = QSqlQuery("CREATE INDEX details_query_index on connections (process, process_args, uid, pid, dst_ip, dst_host, dst_port, action, node, protocol)", self.db) + q.exec_() + + q = QSqlQuery("create table if not exists nodes (" \ + "addr text primary key," \ + "hostname text," \ + "daemon_version text," \ + "daemon_uptime text," \ + "daemon_rules text," \ + "cons text," \ + "cons_dropped text," \ + "version text," \ + "status text, " \ + "last_connection text)" + , self.db) + q.exec_() + + q = QSqlQuery("create table if not exists rules (" \ + "time text, " \ + "node text, " \ + "name text, " \ + "enabled text, " \ + "precedence text, " \ + "action text, " \ + "duration text, " \ + "operator_type text, " \ + "operator_sensitive text, " \ + "operator_operand text, " \ + "operator_data text, " \ + "description text, " \ + "nolog text, " \ + "created text, " \ + "UNIQUE(node, name)" + ")", self.db) + q.exec_() + q = QSqlQuery("create index rules_index on rules (time)", self.db) + q.exec_() + + q = QSqlQuery("create table if not exists hosts (what text primary key, hits integer)", self.db) + q.exec_() + q = QSqlQuery("create table if not exists procs (what text primary key, hits integer)", self.db) + q.exec_() + q = QSqlQuery("create table if not exists addrs (what text primary key, hits integer)", self.db) + q.exec_() + q = QSqlQuery("create table if not exists ports (what text primary key, hits integer)", self.db) + q.exec_() + q = QSqlQuery("create table if not exists users (what text primary key, hits integer)", self.db) + q.exec_() + + def get_schema_version(self): + q = QSqlQuery("PRAGMA user_version;", self.db) + q.exec_() + if q.next(): + print("schema version:", q.value(0)) + return int(q.value(0)) + + return 0 + + def set_schema_version(self, version): + print("setting schema version to:", version) + q = QSqlQuery("PRAGMA user_version = {0}".format(version), self.db) + if q.exec_() == False: + print("Error updating updating schema version:", q.lastError().text()) + + def get_journal_mode(self): + q = QSqlQuery("PRAGMA journal_mode;", self.db) + q.exec_() + if q.next(): + return str(q.value(0)) + + return str("unknown") + + def set_journal_mode(self, mode): + # https://www.sqlite.org/wal.html + mode_str = Database.DB_JOURNAL_MODE_LIST[mode] + if self.get_journal_mode().lower() != mode_str.lower(): + print("Setting journal_mode: ", mode_str) + q = QSqlQuery("PRAGMA journal_mode = {modestr};".format(modestr = mode_str), self.db) + if q.exec_() == False: + print("Error updating PRAGMA journal_mode:", q.lastError().text()) + return False + if mode == 3 or mode == 5: + print("Setting DB memory optimizations") + q = QSqlQuery("PRAGMA synchronous = OFF;", self.db) + if q.exec_() == False: + print("Error updating PRAGMA synchronous:", q.lastError().text()) + return False + q = QSqlQuery("PRAGMA cache_size=10000;", self.db) + if q.exec_() == False: + print("Error updating PRAGMA cache_size:", q.lastError().text()) + return False + else: + print("Setting synchronous = NORMAL") + q = QSqlQuery("PRAGMA synchronous = NORMAL;", self.db) + if q.exec_() == False: + print("Error updating PRAGMA synchronous:", q.lastError().text()) + + return True + + def _upgrade_db_schema(self): + migrations_path = os.path.dirname(os.path.realpath(__file__)) + "/migrations" + schema_version = self.get_schema_version() + if schema_version == self.DB_VERSION: + print("db schema is up to date") + return + while schema_version < self.DB_VERSION: + schema_version += 1 + try: + print("applying schema upgrade:", schema_version) + self._apply_db_upgrade("{0}/upgrade_{1}.sql".format(migrations_path, schema_version)) + except Exception as e: + print("Not applying upgrade_{0}.sql:".format(schema_version), e) + return + self.set_schema_version(schema_version) + + def _apply_db_upgrade(self, file): + print("applying upgrade from:", file) + q = QSqlQuery(self.db) + with open(file) as f: + for line in f.readlines(): + # skip comments + if line.startswith("--"): + continue + print("applying upgrade:", line, end="") + if q.exec(line) == False: + print("\tError:", q.lastError().text()) + else: + print("\tOK") + + def optimize(self): + """https://www.sqlite.org/pragma.html#pragma_optimize + """ + q = QSqlQuery("PRAGMA optimize;", self.db) + q.exec_() + + def clean(self, table): + with self._lock: + q = QSqlQuery("delete from " + table, self.db) + q.exec_() + + def vacuum(self): + q = QSqlQuery("VACUUM;", self.db) + q.exec_() + + def clone_db(self, name): + return QSqlDatabase.cloneDatabase(self.db, name) + + def clone(self): + q = QSqlQuery(".dump", self.db) + q.exec_() + + def transaction(self): + self.db.transaction() + + def commit(self): + self.db.commit() + + def rollback(self): + self.db.rollback() + + def get_total_records(self): + try: + q = QSqlQuery("SELECT count(*) FROM connections", self.db) + if q.exec_() and q.first(): + r = q.value(0) + except Exception as e: + print("db, get_total_records() error:", e) + + def get_newest_record(self): + try: + q = QSqlQuery("SELECT time FROM connections ORDER BY 1 DESC LIMIT 1", self.db) + if q.exec_() and q.first(): + return q.value(0) + except Exception as e: + print("db, get_newest_record() error:", e) + return 0 + + def get_oldest_record(self): + try: + q = QSqlQuery("SELECT time FROM connections ORDER BY 1 ASC LIMIT 1", self.db) + if q.exec_() and q.first(): + return q.value(0) + except Exception as e: + print("db, get_oldest_record() error:", e) + return 0 + + def purge_oldest(self, max_days_to_keep): + try: + oldt = self.get_oldest_record() + newt = self.get_newest_record() + if oldt == None or newt == None or oldt == 0 or newt == 0: + return -1 + + oldest = datetime.strptime(oldt, "%Y-%m-%d %H:%M:%S.%f") + newest = datetime.strptime(newt, "%Y-%m-%d %H:%M:%S.%f") + diff = newest - oldest + date_to_purge = datetime.now() - timedelta(days=max_days_to_keep) + + if diff.days >= max_days_to_keep: + q = QSqlQuery(self.db) + q.prepare("DELETE FROM connections WHERE time < ?") + q.bindValue(0, str(date_to_purge)) + if q.exec_(): + print("purge_oldest() {0} records deleted".format(q.numRowsAffected())) + return q.numRowsAffected() + except Exception as e: + print("db, purge_oldest() error:", e) + + return -1 + + def select(self, qstr): + try: + return QSqlQuery(qstr, self.db) + except Exception as e: + print("db, select() exception: ", e) + + return None + + def remove(self, qstr): + try: + q = QSqlQuery(qstr, self.db) + if q.exec_(): + return True + else: + print("db, remove() ERROR: ", qstr) + print(q.lastError().driverText()) + except Exception as e: + print("db, remove exception: ", e) + + return False + + def _insert(self, query_str, columns): + with self._lock: + try: + + q = QSqlQuery(self.db) + q.prepare(query_str) + for idx, v in enumerate(columns): + q.bindValue(idx, v) + if q.exec_(): + return True + else: + print("_insert() ERROR", query_str) + print(q.lastError().driverText()) + + except Exception as e: + print("_insert exception", e) + finally: + q.finish() + + return False + + def insert(self, table, fields, columns, update_field=None, update_values=None, action_on_conflict="REPLACE"): + if update_field != None: + action_on_conflict = "" + else: + action_on_conflict = "OR " + action_on_conflict + + qstr = "INSERT " + action_on_conflict + " INTO " + table + " " + fields + " VALUES(" + update_fields="" + for col in columns: + qstr += "?," + qstr = qstr[0:len(qstr)-1] + ")" + + if update_field != None: + # NOTE: UPSERTS on sqlite are only supported from v3.24 on. + # On Ubuntu16.04/18 for example (v3.11/3.22) updating a record on conflict + # fails with "Parameter count error" + qstr += " ON CONFLICT (" + update_field + ") DO UPDATE SET " + for idx, field in enumerate(update_values): + qstr += str(field) + "=excluded." + str(field) + "," + + qstr = qstr[0:len(qstr)-1] + + return self._insert(qstr, columns) + + def update(self, table, fields, values, condition=None, action_on_conflict="OR IGNORE"): + qstr = "UPDATE " + action_on_conflict + " " + table + " SET " + fields + if condition != None: + qstr += " WHERE " + condition + try: + with self._lock: + q = QSqlQuery(qstr, self.db) + q.prepare(qstr) + for idx, v in enumerate(values): + q.bindValue(idx, v) + if not q.exec_(): + print("update ERROR:", qstr, "values:", values) + print(q.lastError().driverText()) + + except Exception as e: + print("update() exception:", e) + finally: + q.finish() + + def _insert_batch(self, query_str, fields, values): + result=True + with self._lock: + try: + q = QSqlQuery(self.db) + q.prepare(query_str) + q.addBindValue(fields) + q.addBindValue(values) + if not q.execBatch(): + print("_insert_batch() db error:", query_str) + print(q.lastError().driverText()) + print("\t", fields) + print("\t", values) + + result=False + except Exception as e: + print("_insert_batch() exception:", e) + finally: + q.finish() + + return result + + def insert_batch(self, table, db_fields, db_columns, fields, values, update_field=None, update_value=None, action_on_conflict="REPLACE"): + action = "OR " + action_on_conflict + if update_field != None: + action = "" + + qstr = "INSERT " + action + " INTO " + table + " (" + db_fields[0] + "," + db_fields[1] + ") VALUES(" + for idx in db_columns: + qstr += "?," + qstr = qstr[0:len(qstr)-1] + ")" + + if self._insert_batch(qstr, fields, values) == False: + self.update_batch(table, db_fields, db_columns, fields, values, update_field, update_value, action_on_conflict) + + def update_batch(self, table, db_fields, db_columns, fields, values, update_field=None, update_value=None, action_on_conflict="REPLACE"): + for idx, i in enumerate(values): + s = "UPDATE " + table + " SET " + "%s=(select hits from %s)+%s" % (db_fields[1], table, values[idx]) + s += " WHERE %s=\"%s\"," % (db_fields[0], fields[idx]) + s = s[0:len(s)-1] + with self._lock: + q = QSqlQuery(s, self.db) + if not q.exec_(): + print("update batch ERROR", s) + print(q.lastError().driverText()) + + def dump(self): + q = QSqlQuery(".dump", db=self.db) + q.exec_() + + def get_query(self, table, fields): + return "SELECT " + fields + " FROM " + table + + def empty_rule(self, name=""): + if name == "": + return + qstr = "DELETE FROM connections WHERE rule = ?" + + with self._lock: + q = QSqlQuery(qstr, self.db) + q.prepare(qstr) + q.addBindValue(name) + if not q.exec_(): + print("db, empty_rule() ERROR: ", qstr) + print(q.lastError().driverText()) + + def delete_rule(self, name, node_addr): + qstr = "DELETE FROM rules WHERE name=?" + if node_addr != None: + qstr = qstr + " AND node=?" + + with self._lock: + q = QSqlQuery(qstr, self.db) + q.prepare(qstr) + q.addBindValue(name) + if node_addr != None: + q.addBindValue(node_addr) + if not q.exec_(): + print("db, delete_rule() ERROR: ", qstr) + print(q.lastError().driverText()) + return False + + return True + + def delete_rules_by_field(self, field, values): + if len(values) == 0: + return True + + qstr = "DELETE FROM rules WHERE " + for v in values: + qstr += field + "=? OR " + + qstr = qstr[:-4] + + with self._lock: + q = QSqlQuery(qstr, self.db) + q.prepare(qstr) + + for v in values: + q.addBindValue(v) + + if not q.exec_(): + print("db, delete_rule_by_field() ERROR: ", qstr) + print(q.lastError().driverText()) + return False + + return True + + def get_connection_by_field(self, field, date): + """ + """ + qstr = "SELECT * FROM connections WHERE {0}=?".format(field) + + q = QSqlQuery(qstr, self.db) + q.prepare(qstr) + q.addBindValue(date) + q.exec_() + + return q + + def get_rule(self, rule_name, node_addr=None): + """ + get rule records, given the name of the rule and the node + """ + qstr = "SELECT * FROM rules WHERE name=?" + if node_addr != None: + qstr = qstr + " AND node=?" + + q = QSqlQuery(qstr, self.db) + q.prepare(qstr) + q.addBindValue(rule_name) + if node_addr != None: + q.addBindValue(node_addr) + q.exec_() + + return q + + def get_rules(self, node_addr): + """ + get rule records, given the name of the rule and the node + """ + qstr = "SELECT * FROM rules WHERE node=?" + q = QSqlQuery(qstr, self.db) + q.prepare(qstr) + q.addBindValue(node_addr) + if not q.exec_(): + return None + + return q + + def insert_rule(self, rule, node_addr): + self.insert("rules", + "(time, node, name, description, enabled, precedence, nolog, action, duration, operator_type, operator_sensitive, operator_operand, operator_data)", + (datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + node_addr, rule.name, rule.description, + str(rule.enabled), str(rule.precedence), str(rule.nolog), + rule.action, rule.duration, rule.operator.type, + str(rule.operator.sensitive), rule.operator.operand, rule.operator.data), + action_on_conflict="IGNORE") + + def rule_exists(self, rule, node_addr): + qstr = "SELECT node, name, action, duration, operator_type, operator_operand, operator_data " \ + " FROM rules WHERE " \ + "name=? AND " \ + "node=? AND " \ + "action=? AND " \ + "duration=? AND " \ + "operator_type=? AND " \ + "operator_operand=? AND " \ + "operator_data=?" + q = QSqlQuery(qstr, self.db) + q.prepare(qstr) + q.addBindValue(rule.name) + q.addBindValue(node_addr) + q.addBindValue(rule.action) + q.addBindValue(rule.duration) + q.addBindValue(rule.operator.type) + q.addBindValue(rule.operator.operand) + q.addBindValue(rule.operator.data) + if not q.exec_() or q.next() == False: + return None + + return q diff --git a/ui/opensnitch/database/enums.py b/ui/opensnitch/database/enums.py new file mode 100644 index 0000000..72e98dc --- /dev/null +++ b/ui/opensnitch/database/enums.py @@ -0,0 +1,34 @@ + +class ConnFields(): + Time = 0 + Node = 1 + Action = 2 + Protocol = 3 + SrcIP = 4 + SrcPort = 5 + DstIP = 6 + DstHost = 7 + DstPort = 8 + UID = 9 + PID = 10 + Process = 11 + Cmdline = 12 + CWD = 13 + Rule = 14 + +class RuleFields(): + """These fields must be in the order defined in the DB""" + Time = 0 + Node = 1 + Name = 2 + Enabled = 3 + Precedence = 4 + Action = 5 + Duration = 6 + OpType = 7 + OpSensitive = 8 + OpOperand = 9 + OpData = 10 + Description = 11 + NoLog = 12 + Created = 13 diff --git a/ui/opensnitch/database/migrations/upgrade_1.sql b/ui/opensnitch/database/migrations/upgrade_1.sql new file mode 100644 index 0000000..8a35251 --- /dev/null +++ b/ui/opensnitch/database/migrations/upgrade_1.sql @@ -0,0 +1 @@ +ALTER TABLE rules ADD COLUMN description; diff --git a/ui/opensnitch/database/migrations/upgrade_2.sql b/ui/opensnitch/database/migrations/upgrade_2.sql new file mode 100644 index 0000000..73a5ad5 --- /dev/null +++ b/ui/opensnitch/database/migrations/upgrade_2.sql @@ -0,0 +1 @@ +ALTER TABLE rules ADD COLUMN nolog; diff --git a/ui/opensnitch/database/migrations/upgrade_3.sql b/ui/opensnitch/database/migrations/upgrade_3.sql new file mode 100644 index 0000000..60aa2dc --- /dev/null +++ b/ui/opensnitch/database/migrations/upgrade_3.sql @@ -0,0 +1 @@ +ALTER TABLE rules ADD COLUMN created; diff --git a/ui/opensnitch/desktop_parser.py b/ui/opensnitch/desktop_parser.py new file mode 100644 index 0000000..fbbc028 --- /dev/null +++ b/ui/opensnitch/desktop_parser.py @@ -0,0 +1,194 @@ +from threading import Lock +import configparser +import threading +import glob +import os +import re +import shutil +import locale + +is_pyinotify_available = True +try: + import pyinotify +except Exception as e: + is_pyinotify_available = False + print("Error importing pyinotify:", e) + +DESKTOP_PATHS = tuple([ + os.path.join(d, 'applications') + for d in os.getenv('XDG_DATA_DIRS', '/usr/share/').split(':') +]) + +class LinuxDesktopParser(threading.Thread): + def __init__(self): + threading.Thread.__init__(self) + self.lock = Lock() + self.daemon = True + self.running = False + self.apps = {} + self.apps_by_name = {} + self.get_locale() + # some things are just weird + # (not really, i don't want to keep track of parent pids + # just because of icons though, this hack is way easier) + self.fixes = { + '/opt/google/chrome/chrome': '/opt/google/chrome/google-chrome', + '/usr/lib/firefox/firefox': '/usr/lib/firefox/firefox.sh', + '/usr/bin/pidgin.orig': '/usr/bin/pidgin' + } + + for desktop_path in DESKTOP_PATHS: + if not os.path.exists(desktop_path): + continue + for desktop_file in glob.glob(os.path.join(desktop_path, '*.desktop')): + self._parse_desktop_file(desktop_file) + + if is_pyinotify_available: + self.start() + + def get_locale(self): + try: + self.locale = locale.getlocale()[0] + self.locale_country = self.locale.split("_")[0] + except Exception: + self.locale = "" + self.locale_country = "" + + def _parse_exec(self, cmd): + try: + is_flatpak = re.search(r'^/usr/[s]*bin/flatpak.*--command=([a-zA-Z0-9-_\/\.\+]+)', cmd) + if is_flatpak: + return is_flatpak.group(1) + + # remove stuff like %U + cmd = re.sub( r'%[a-zA-Z]+', '', cmd) + # remove 'env .... command' + cmd = re.sub( r'^env\s+[^\s]+\s', '', cmd) + # split && trim + cmd = cmd.split(' ')[0].strip() + # remove quotes + cmd = re.sub( r'["\']+', '', cmd) + + # check if we need to resolve the path + if len(cmd) > 0 and cmd[0] != '/': + for path in os.environ["PATH"].split(os.pathsep): + filename = os.path.join(path, cmd) + if os.path.exists(filename): + cmd = filename + break + + except Exception as e: + print("desktop_parser._parse_exec() exception:", e) + + return cmd + + def get_app_description(self, parser): + try: + desc = parser.get('Desktop Entry', 'Comment[%s]' % self.locale_country, raw=True, fallback=None) + if desc == None: + desc = parser.get('Desktop Entry', 'Comment[%s]' % self.locale, raw=True, fallback=None) + + if desc == None: + desc = parser.get('Desktop Entry', 'Comment', raw=True, fallback=None) + + return desc + except: + return None + + def discover_app_icon(self, app_name): + # more hacks + # normally qt will find icons if the system if configured properly. + # if it's not, qt won't be able to find the icon by using QIcon().fromTheme(""), + # so we fallback to try to determine if the icon exist in some well known system paths. + icon_dirs = ("/usr/share/icons/gnome/48x48/apps/", "/usr/share/pixmaps/", "/usr/share/icons/hicolor/48x48/apps/", "/usr/share/icons/HighContrast/48x48/apps/") + icon_exts = (".png", ".xpm", ".svg") + for idir in icon_dirs: + for iext in icon_exts: + if iext in app_name: + iconPath = idir + app_name + if os.path.exists(iconPath): + return iconPath + else: + iconPath = idir + app_name + iext + if os.path.exists(iconPath): + return iconPath + + def _parse_desktop_file(self, desktop_path): + parser = configparser.ConfigParser(strict=False) # Allow duplicate config entries + try: + basename = os.path.basename(desktop_path)[:-8] + parser.read(desktop_path, 'utf8') + + cmd = parser.get('Desktop Entry', 'exec', raw=True, fallback=None) + if cmd == None: + cmd = parser.get('Desktop Entry', 'Exec', raw=True, fallback=None) + if cmd is not None: + cmd = self._parse_exec(cmd) + icon = parser.get('Desktop Entry', 'Icon', raw=True, fallback=None) + name = parser.get('Desktop Entry', 'Name', raw=True, fallback=None) + desc = self.get_app_description(parser) + + if icon == None: + # Some .desktop files doesn't have the Icon entry + # FIXME: even if we return an icon, if the DE is not properly configured, + # it won't be loaded/displayed. + icon = self.discover_app_icon(basename) + + with self.lock: + # The Exec entry may have an absolute path to a binary or just the binary with parameters. + # /path/binary or binary, so save both + self.apps[cmd] = (name, icon, desc, desktop_path) + self.apps[basename] = (name, icon, desc, desktop_path) + # if the command is a symlink, add the real binary too + if os.path.islink(cmd): + link_to = os.path.realpath(cmd) + self.apps[link_to] = (name, icon, desc, desktop_path) + except: + print("Exception parsing .desktop file ", desktop_path) + + def get_info_by_path(self, path, default_icon): + def_name = os.path.basename(path) + # apply fixes + for orig, to in self.fixes.items(): + if path == orig: + path = to + break + + app_name = self.apps.get(path) + if app_name == None: + # last try to get a default terminal icon + for def_icon in ("utilities-terminal", "gnome-terminal", "xfce-terminal"): + test = self.apps.get(def_name, (def_name, def_icon, "", None)) + if test != None: + return test + + return self.apps.get(def_name, (def_name, default_icon, "", None)) + + return self.apps.get(path, (def_name, default_icon, "", None)) + + def get_info_by_binname(self, name, default_icon): + def_name = os.path.basename(name) + return self.apps.get(def_name, (def_name, default_icon, None)) + + def run(self): + self.running = True + wm = pyinotify.WatchManager() + notifier = pyinotify.Notifier(wm) + + def inotify_callback(event): + if event.mask == pyinotify.IN_CLOSE_WRITE: + self._parse_desktop_file(event.pathname) + + elif event.mask == pyinotify.IN_DELETE: + with self.lock: + for cmd, data in self.apps.items(): + if data[2] == event.pathname: + del self.apps[cmd] + break + + for p in DESKTOP_PATHS: + if os.path.exists(p): + wm.add_watch(p, + pyinotify.IN_CLOSE_WRITE | pyinotify.IN_DELETE, + inotify_callback) + notifier.loop() diff --git a/ui/opensnitch/dialogs/__init__.py b/ui/opensnitch/dialogs/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ui/opensnitch/dialogs/conndetails.py b/ui/opensnitch/dialogs/conndetails.py new file mode 100644 index 0000000..98dbe92 --- /dev/null +++ b/ui/opensnitch/dialogs/conndetails.py @@ -0,0 +1,60 @@ + +from opensnitch.nodes import Nodes +from opensnitch.database import Database +from opensnitch.database.enums import ConnFields +from opensnitch.utils import Utils +from opensnitch.utils.infowindow import InfoWindow +from PyQt5.QtCore import QCoreApplication as QC + +class ConnDetails(InfoWindow): + """Display a small dialog with the details of a connection + """ + + def __init__(self, parent): + super().__init__(parent) + + self._db = Database.instance() + self._nodes = Nodes.instance() + + def showByField(self, field, value): + records = self._db.get_connection_by_field(field, value) + if not records.next(): + return + + node = records.value(ConnFields.Node) + uid = records.value(ConnFields.UID) + if self._nodes.is_local(node): + uid = Utils.get_user_id(uid) + + conn_text = QC.translate("stats", """ +<b>{0}</b><br><br> +<b>Time:</b> {1}<br><br> +<b>Process:</b><br>{2}<br> +<b>Cmdline:</b><br>{3}<br> +<b>CWD:</b><br>{4}<br><br> +<b>UID:</b> {5} <b>PID:</b> {6}<br> +<br> +<b>Node:</b> {7}<br><br> +<b>{8}</b> {9}:{10} -> {11} ({12}):{13} +<br><br> +<b>Rule:</b><br> +{14} +""".format( + records.value(ConnFields.Action).upper(), + records.value(ConnFields.Time), + records.value(ConnFields.Process), + records.value(ConnFields.Cmdline), + records.value(ConnFields.CWD), + uid, + records.value(ConnFields.PID), + node, + records.value(ConnFields.Protocol).upper(), + records.value(ConnFields.SrcPort), + records.value(ConnFields.SrcIP), + records.value(ConnFields.DstIP), + records.value(ConnFields.DstHost), + records.value(ConnFields.DstPort), + records.value(ConnFields.Rule) + )) + + self.showText(conn_text) diff --git a/ui/opensnitch/dialogs/firewall.py b/ui/opensnitch/dialogs/firewall.py new file mode 100644 index 0000000..8b75493 --- /dev/null +++ b/ui/opensnitch/dialogs/firewall.py @@ -0,0 +1,398 @@ +import sys +import time +import os +import os.path +import json + +from PyQt5 import QtCore, QtGui, uic, QtWidgets +from PyQt5.QtCore import QCoreApplication as QC + +from opensnitch.utils import Icons, Message +from opensnitch.config import Config +from opensnitch.nodes import Nodes +from opensnitch.dialogs.firewall_rule import FwRuleDialog +import opensnitch.firewall as Fw +import opensnitch.firewall.profiles as FwProfiles + +import opensnitch.proto as proto +ui_pb2, ui_pb2_grpc = proto.import_() + +DIALOG_UI_PATH = "%s/../res/firewall.ui" % os.path.dirname(sys.modules[__name__].__file__) +class FirewallDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]): + LOG_TAG = "[fw dialog]" + + COMBO_IN = 0 + COMBO_OUT = 1 + + POLICY_ACCEPT = 0 + POLICY_DROP = 1 + + _notification_callback = QtCore.pyqtSignal(ui_pb2.NotificationReply) + + def __init__(self, parent=None, appicon=None, node=None): + QtWidgets.QDialog.__init__(self, parent) + self.setupUi(self) + self.setWindowIcon(appicon) + self.appicon = appicon + + # TODO: profiles are ready to be used. They need to be tested, and + # create some default profiles (home, office, public, ...) + self.comboProfile.setVisible(False) + self.lblProfile.setVisible(False) + + self.secHighIcon = Icons.new(self, "security-high") + self.secMediumIcon = Icons.new(self, "security-medium") + self.secLowIcon = Icons.new(self, "security-low") + self.lblStatusIcon.setPixmap(self.secHighIcon.pixmap(96, 96)) + + self._fwrule_dialog = FwRuleDialog(appicon=self.appicon) + self._cfg = Config.get() + self._fw = Fw.Firewall.instance() + self._nodes = Nodes.instance() + self._fw_profiles = {} + self._last_profile = { + self.COMBO_IN: FwProfiles.ProfileAcceptInput.value, + self.COMBO_OUT: FwProfiles.ProfileAcceptInput.value + } + + self._notification_callback.connect(self._cb_notification_callback) + self._notifications_sent = {} + + self._nodes.nodesUpdated.connect(self._cb_nodes_updated) + self.cmdNewRule.clicked.connect(self._cb_new_rule_clicked) + self.cmdAllowOUTService.clicked.connect(self._cb_allow_out_service_clicked) + self.cmdAllowINService.clicked.connect(self._cb_allow_in_service_clicked) + self.comboInput.currentIndexChanged.connect(lambda: self._cb_combo_policy_changed(self.COMBO_IN)) + self.comboProfile.currentIndexChanged.connect(self._cb_combo_profile_changed) + self.sliderFwEnable.valueChanged.connect(self._cb_enable_fw_changed) + self.cmdClose.clicked.connect(self._cb_close_clicked) + self.cmdHelp.clicked.connect( + lambda: QtGui.QDesktopServices.openUrl(QtCore.QUrl(Config.HELP_SYSFW_URL)) + ) + + # TODO: when output policy is set to Drop, all outbound traffic is + # blocked. + #self.comboOutput.currentIndexChanged.connect(lambda: self._cb_combo_policy_changed(self.COMBO_OUT)) + + if QtGui.QIcon.hasThemeIcon("document-new"): + return + + closeIcon = Icons.new(self, "window-close") + excludeIcon = Icons.new(self, "go-up") + allowInIcon = Icons.new(self, "go-down") + newIcon = Icons.new(self, "document-new") + helpIcon = Icons.new(self, "help-browser") + self.cmdClose.setIcon(closeIcon) + self.cmdAllowOUTService.setIcon(excludeIcon) + self.cmdAllowINService.setIcon(allowInIcon) + self.cmdNewRule.setIcon(newIcon) + self.cmdHelp.setIcon(helpIcon) + + @QtCore.pyqtSlot(ui_pb2.NotificationReply) + def _cb_notification_callback(self, reply): + self.comboInput.setEnabled(True) + if reply.id in self._notifications_sent: + if reply.code == ui_pb2.OK: + rep = self._notifications_sent[reply.id] + self._set_status_successful(QC.translate("firewall", "Configuration applied.")) + + else: + self._set_status_error(QC.translate("firewall", "Error: {0}").format(reply.data)) + + del self._notifications_sent[reply.id] + else: + print(self.LOG_TAG, "unknown notification:", reply) + + + @QtCore.pyqtSlot(int) + def _cb_nodes_updated(self, total): + self._check_fw_status() + + def _cb_combo_profile_changed(self, idx): + combo_profile = self._fw_profiles[idx] + json_profile = json.dumps(list(combo_profile.values())[0]['Profile']) + + for addr in self._nodes.get(): + fwcfg = self._nodes.get_node(addr)['firewall'] + ok, err = self._fw.apply_profile(addr, json_profile) + if ok: + self.send_notification(addr, fwcfg) + else: + self._set_status_error(QC.translate("firewall", "error adding profile extra rules:", err)) + + def _cb_combo_policy_changed(self, combo): + self._reset_status_message() + self.comboInput.setEnabled(False) + + wantedProfile = FwProfiles.ProfileAcceptInput.value + if combo == self.COMBO_OUT: + wantedProfile = FwProfiles.ProfileAcceptOutput.value + if self.comboOutput.currentIndex() == self.POLICY_DROP: + wantedProfile = FwProfiles.ProfileDropOutput.value + else: + if self.comboInput.currentIndex() == self.POLICY_DROP: + wantedProfile = FwProfiles.ProfileDropInput.value + + + if combo == self.COMBO_IN and \ + self.comboInput.currentIndex() == self.POLICY_ACCEPT: + json_profile = json.dumps(FwProfiles.ProfileDropInput.value) + for addr in self._nodes.get(): + fwcfg = self._nodes.get_node(addr)['firewall'] + ok, err = self._fw.delete_profile(addr, json_profile) + if not ok: + print(err) + + + json_profile = json.dumps(wantedProfile) + for addr in self._nodes.get(): + fwcfg = self._nodes.get_node(addr)['firewall'] + ok, err = self._fw.apply_profile(addr, json_profile) + if ok: + self.send_notification(addr, fwcfg) + else: + self._set_status_error(QC.translate("firewall", "Policy not applied: {0}".format(err))) + + self._last_profile[combo] = wantedProfile + + def _cb_new_rule_clicked(self): + self.new_rule() + + def _cb_allow_out_service_clicked(self): + self.allow_out_service() + + def _cb_allow_in_service_clicked(self): + self.allow_in_service() + + def _cb_enable_fw_changed(self, enable): + if self._nodes.count() == 0: + self.sliderFwEnable.blockSignals(True) + self.sliderFwEnable.setValue(False) + self.sliderFwEnable.blockSignals(False) + return + self.enable_fw(enable) + + def _cb_close_clicked(self): + self._close() + + def _load_nodes(self): + self._nodes = self._nodes.get() + + def _close(self): + self.hide() + + def _change_fw_backend(self, addr, node_cfg): + nid, notif = self._nodes.change_node_config(addr, node_cfg, self._notification_callback) + self._notifications_sent[nid] = notif + + def showEvent(self, event): + super(FirewallDialog, self).showEvent(event) + self._reset_fields() + self._check_fw_status() + self._fw_profiles = FwProfiles.Profiles.load_predefined_profiles() + self.comboProfile.blockSignals(True) + for pr in self._fw_profiles: + self.comboProfile.addItem([pr[k] for k in pr][0]['Name']) + self.comboProfile.blockSignals(False) + + def send_notification(self, node_addr, fw_config): + self._set_status_message(QC.translate("firewall", "Applying changes...")) + nid, notif = self._nodes.reload_fw(node_addr, fw_config, self._notification_callback) + self._notifications_sent[nid] = {'addr': node_addr, 'notif': notif} + + def _check_fw_status(self): + self.lblFwStatus.setText("") + self.sliderFwEnable.blockSignals(True) + self.comboInput.blockSignals(True) + self.comboOutput.blockSignals(True) + self.comboProfile.blockSignals(True) + + self._disable_widgets() + + try: + enableFw = False + enableFwBtn = (self._nodes.count() > 0) + self.sliderFwEnable.blockSignals(True) + self.sliderFwEnable.setEnabled(enableFwBtn) + self.sliderFwEnable.blockSignals(False) + if not enableFwBtn: + return + + # TODO: handle nodes' firewall properly + for addr in self._nodes.get(): + node = self._nodes.get_node(addr) + self._fwConfig = node['firewall'] + enableFw |= self._fwConfig.Enabled + + if self.fw_is_incompatible(addr, node): + enableFw = False + return + + # XXX: Here we loop twice over the chains. We could have 1 loop. + pol_in = self._fw.chains.get_policy(addr, Fw.Hooks.INPUT.value) + pol_out = self._fw.chains.get_policy(addr, Fw.Hooks.OUTPUT.value) + + if pol_in != None: + self.comboInput.setCurrentIndex( + Fw.Policy.values().index(pol_in) + ) + else: + self._set_status_error(QC.translate("firewall", "Error getting INPUT chain policy")) + self._disable_widgets() + if pol_out != None: + self.comboOutput.setCurrentIndex( + Fw.Policy.values().index(pol_out) + ) + else: + self._set_status_error(QC.translate("firewall", "Error getting OUTPUT chain policy")) + self._disable_widgets() + + except Exception as e: + self._set_status_error("Firewall status error (report on github please): {0}".format(e)) + + finally: + # some nodes may have the firewall disabled whilst other enabled + #if not enableFw: + # self.lblFwStatus(QC.translate("firewall", "Some nodes have the firewall disabled")) + + self._disable_widgets(not enableFw) + self.lblStatusIcon.setEnabled(enableFw) + self.sliderFwEnable.setValue(enableFw) + self.sliderFwEnable.blockSignals(False) + self.comboInput.blockSignals(False) + self.comboOutput.blockSignals(False) + self.comboProfile.blockSignals(False) + + def fw_is_incompatible(self, addr, node): + """Check if the fw is compatible with this GUI. + If it's incompatible, disable the option to enable it. + """ + incompatible = False + # firewall iptables is not supported from the GUI. + # display a warning + node_cfg = json.loads(node['data'].config) + if node_cfg['Firewall'] == "iptables": + self._disable_widgets() + self.sliderFwEnable.setEnabled(False) + if self.isHidden() == False and self.change_fw(addr, node_cfg): + node_cfg['Firewall'] = "nftables" + self.sliderFwEnable.setEnabled(True) + self.enable_fw(True) + self._change_fw_backend(addr, node_cfg) + return False + incompatible = True + + if node['data'].systemFirewall.Version == 0: + self._disable_widgets() + self.sliderFwEnable.setEnabled(False) + self.lblFwStatus.setText( + QC.translate("firewall", "<html>The firewall configuration is outdated,\n" + "you need to update it to the new format: <a href=\"{0}\">learn more</a>" + "</html>".format(Config.HELP_SYS_RULES_URL) + )) + incompatible = True + + return incompatible + + def change_fw(self, addr, node_cfg): + """Ask the user to change fw iptables to nftables + """ + ret = Message.yes_no( + QC.translate("firewall", + "In order to configure firewall rules from the GUI, we need to use 'nftables' instead of 'iptables'" + ), + QC.translate("firewall", "Change default firewall to 'nftables' on node {0}?".format(addr)), + QtWidgets.QMessageBox.Warning) + if ret != QtWidgets.QMessageBox.Cancel: + return True + + return False + + def enable_fw(self, enable): + try: + self._disable_widgets(not enable) + if enable: + self._set_status_message(QC.translate("firewall", "Enabling firewall...")) + else: + self._set_status_message(QC.translate("firewall", "Disabling firewall...")) + + # if previous input policy was DROP, when disabling the firewall it + # must be ACCEPT to allow output traffic. + if not enable and self.comboInput.currentIndex() == self.POLICY_DROP: + self.comboInput.blockSignals(True) + self.comboInput.setCurrentIndex(self.POLICY_ACCEPT) + self.comboInput.blockSignals(False) + for addr in self._nodes.get(): + json_profile = json.dumps(FwProfiles.ProfileAcceptInput.value) + ok, err = self._fw.apply_profile(addr, json_profile) + if not ok: + self._set_status_error( + QC.translate("firewall", "Error applying INPUT ACCEPT profile: {0}".format(err)) + ) + return + + for addr in self._nodes.get(): + # FIXME: + # Due to how the daemon reacts to events when the fw configuration + # is modified, changing the policy + disabling the fw doesn't work + # as expected. + # The daemon detects that the fw is disabled, and it never changes + # the policy. + # As a workaround to this problem, we send 2 fw changes: + # - one for changing the policy + # - another one for disabling the fw + + fwcfg = self._nodes.get_node(addr)['firewall'] + self.send_notification(addr, fwcfg) + time.sleep(0.5) + fwcfg.Enabled = True if enable else False + self.send_notification(addr, fwcfg) + + self.lblStatusIcon.setEnabled(enable) + self.policiesBox.setEnabled(enable) + + time.sleep(0.5) + + except Exception as e: + QC.translate("firewall", "Error: {0}".format(e)) + + def load_rule(self, addr, uuid): + self._fwrule_dialog.load(addr, uuid) + + def new_rule(self): + self._fwrule_dialog.new() + + def allow_out_service(self): + self._fwrule_dialog.exclude_service(self.COMBO_OUT) + + def allow_in_service(self): + self._fwrule_dialog.exclude_service(self.COMBO_IN) + + def _set_status_error(self, msg): + self.statusLabel.show() + self.statusLabel.setStyleSheet('color: red') + self.statusLabel.setText(msg) + + def _set_status_successful(self, msg): + self.statusLabel.show() + self.statusLabel.setStyleSheet('color: green') + self.statusLabel.setText(msg) + + def _set_status_message(self, msg): + self.statusLabel.show() + self.statusLabel.setStyleSheet('color: darkorange') + self.statusLabel.setText(msg) + + def _reset_status_message(self): + self.statusLabel.setText("") + self.statusLabel.hide() + + def _reset_fields(self): + self._reset_status_message() + + def _disable_widgets(self, disable=True): + self.comboInput.setEnabled(not disable) + #self.comboOutput.setEnabled(not disable) + self.cmdNewRule.setEnabled(not disable) + self.cmdAllowOUTService.setEnabled(not disable) + self.cmdAllowINService.setEnabled(not disable) diff --git a/ui/opensnitch/dialogs/firewall_rule.py b/ui/opensnitch/dialogs/firewall_rule.py new file mode 100644 index 0000000..afa85d2 --- /dev/null +++ b/ui/opensnitch/dialogs/firewall_rule.py @@ -0,0 +1,1573 @@ +import sys +import os +import os.path +import ipaddress + +from PyQt5 import QtCore, QtGui, uic, QtWidgets +from PyQt5.QtCore import QCoreApplication as QC + +from opensnitch.config import Config +from opensnitch.nodes import Nodes +from opensnitch.utils import NetworkServices, NetworkInterfaces, QuickHelp, Icons, Utils +import opensnitch.firewall as Fw +from opensnitch.firewall.utils import Utils as FwUtils + +import opensnitch.proto as proto +ui_pb2, ui_pb2_grpc = proto.import_() + +DIALOG_UI_PATH = "%s/../res/firewall_rule.ui" % os.path.dirname(sys.modules[__name__].__file__) +class FwRuleDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]): + LOG_TAG = "[fw rule dialog]" + ACTION_IDX_DENY = 0 + ACTION_IDX_ALLOW = 1 + + IN = 0 + OUT = 1 + FORWARD = 2 + PREROUTING = 3 + POSTROUTING = 4 + + OP_NEW = 0 + OP_SAVE = 1 + OP_DELETE = 2 + + FORM_TYPE_SIMPLE = 0 + FORM_TYPE_EXCLUDE_SERVICE = 1 + FORM_TYPE_ALLOW_IN_SERVICE = 2 + FORM_TYPE = FORM_TYPE_SIMPLE + + STATM_DPORT = 0 + STATM_SPORT = 1 + STATM_DEST_IP = 2 + STATM_SOURCE_IP = 3 + STATM_IIFNAME = 4 + STATM_OIFNAME = 5 + STATM_CT_SET = 6 + STATM_CT_MARK = 7 + STATM_CT_STATE = 8 + STATM_META_SET_MARK = 9 + STATM_META = 10 + STATM_ICMP = 11 + STATM_ICMPv6 = 12 + STATM_LOG = 13 + STATM_QUOTA = 14 + STATM_COUNTER = 15 + STATM_LIMIT = 16 + + _notification_callback = QtCore.pyqtSignal(ui_pb2.NotificationReply) + + def __init__(self, parent=None, appicon=None): + QtWidgets.QDialog.__init__(self, parent) + self.setupUi(self) + self.setWindowIcon(appicon) + + self._fw = Fw.Firewall.instance() + self._nodes = Nodes.instance() + self.net_srv = NetworkServices() + self.statements = {} + self.st_num = 0 + + self.STATM_LIST = [ + "", + QC.translate("firewall", "Dest Port"), + QC.translate("firewall", "Source Port"), + QC.translate("firewall", "Dest IP"), + QC.translate("firewall", "Source IP"), + QC.translate("firewall", "Input interface"), + QC.translate("firewall", "Output interface"), + QC.translate("firewall", "Set conntrack mark"), + QC.translate("firewall", "Match conntrack mark"), + QC.translate("firewall", "Match conntrack state(s)"), + QC.translate("firewall", "Set mark on packet"), + QC.translate("firewall", "Match packet information"), + #"TCP", + #"UDP", + "ICMP", + "ICMPv6", + "LOG", + QC.translate("firewall", "Bandwidth quotas"), + "COUNTER", + QC.translate("firewall", "Rate limit connections"), + ] + + self.STATM_CONF = { + self.STATM_DPORT: { + 'name': Fw.Statements.TCP.value, # tcp, udp, dccp, sctp + 'tooltip': QC.translate("firewall", """ +Supported formats: + + - Simple: 23 + - Ranges: 80-1024 + - Multiple ports: 80,443,8080 +"""), + 'keys': [ + {'key': Fw.Statements.DPORT.value, 'values': self.net_srv.to_array()} + ] + }, + self.STATM_SPORT: { + 'name': Fw.Statements.TCP.value, + 'tooltip': QC.translate("firewall", """ +Supported formats: + + - Simple: 23 + - Ranges: 80-1024 + - Multiple ports: 80,443,8080 +"""), + 'keys': [ + {'key': Fw.Statements.SPORT.value, 'values': self.net_srv.to_array()} + ] + }, + self.STATM_DEST_IP: { + 'name': Fw.Statements.IP.value, # ip or ip6 + 'tooltip': QC.translate("firewall", """ +Supported formats: + + - Simple: 1.2.3.4 + - IP ranges: 1.2.3.100-1.2.3.200 + - Network ranges: 1.2.3.4/24 +"""), + 'keys': [ + {'key': Fw.Statements.DADDR.value, 'values': []} + ] + }, + self.STATM_SOURCE_IP: { + 'name': Fw.Statements.IP.value, + 'tooltip': QC.translate("firewall", """ +Supported formats: + + - Simple: 1.2.3.4 + - IP ranges: 1.2.3.100-1.2.3.200 + - Network ranges: 1.2.3.4/24 +"""), + 'keys': [ + {'key': Fw.Statements.SADDR.value, 'values': []} + ] + }, + self.STATM_IIFNAME: { + 'name': Fw.Statements.IIFNAME.value, + 'tooltip': QC.translate("firewall", """Match input interface. Regular expressions not allowed. +Use * to match multiple interfaces."""), + 'keys': [ + {'key': "", 'values': []} + ] + }, + self.STATM_OIFNAME: { + 'name': Fw.Statements.OIFNAME.value, + 'tooltip': QC.translate("firewall", """Match output interface. Regular expressions not allowed. +Use * to match multiple interfaces."""), + 'keys': [ + {'key': "", 'values': []} + ] + }, + self.STATM_CT_SET: { + 'name': Fw.Statements.CT.value, + 'tooltip': QC.translate("firewall", "Set a conntrack mark on the connection, in decimal format."), + 'keys': [ + # we need 2 keys for this expr: key: set, value: <empty>, key: mark, value: xxx + {'key': Fw.ExprCt.SET.value, 'values': None}, # must be empty + {'key': Fw.ExprCt.MARK.value, 'values': []} + ] + }, + # match mark + self.STATM_CT_MARK: { + 'name': Fw.Statements.CT.value, + 'tooltip': QC.translate("firewall", "Match a conntrack mark of the connection, in decimal format."), + 'keys': [ + {'key': Fw.ExprCt.MARK.value, 'values': []} + ] + }, + self.STATM_CT_STATE: { + 'name': Fw.Statements.CT.value, + 'tooltip': QC.translate("firewall", """Match conntrack states. + +Supported formats: + - Simple: new + - Multiple states separated by commas: related,new +"""), + 'keys': [ + { + 'key': Fw.ExprCt.STATE.value, + 'values': [Fw.ExprCt.NEW.value, Fw.ExprCt.ESTABLISHED.value, Fw.ExprCt.RELATED.value, Fw.ExprCt.INVALID.value] + } + ] + }, + self.STATM_META: { + 'name': Fw.Statements.META.value, + 'tooltip': QC.translate("firewall", """ +Match packet's metainformation. + +Value must be in decimal format, except for the "l4proto" option. +For l4proto it can be a lower case string, for example: + tcp + udp + icmp, + etc + +If the value is decimal for protocol or lproto, it'll use it as the code of +that protocol. +"""), + 'keys': [ + {'key': Fw.ExprMeta.MARK.value, 'values': []}, + {'key': Fw.ExprMeta.L4PROTO.value, 'values': Fw.Protocols.values()} + ] + }, + self.STATM_META_SET_MARK: { + 'name': Fw.Statements.META.value, + 'tooltip': QC.translate("firewall", "Set a mark on the packet matching the specified conditions. The value is in decimal format."), + 'keys': [ + {'key': Fw.ExprMeta.SET.value, 'values': None}, + {'key': Fw.ExprMeta.MARK.value, 'values': []} + ] + }, + self.STATM_ICMP: { + 'name': Fw.Statements.ICMP.value, + 'tooltip': QC.translate("firewall", """ +Match ICMP codes. + +Supported formats: + - Simple: echo-request + - Multiple separated by commas: echo-request,echo-reply +"""), + 'keys': [ + {'key': "type", 'values': Fw.ExprICMP.values()} + ] + }, + self.STATM_ICMPv6: { + 'name': Fw.Statements.ICMPv6.value, + 'tooltip': QC.translate("firewall", """ +Match ICMPv6 codes. + +Supported formats: + - Simple: echo-request + - Multiple separated by commas: echo-request,echo-reply +"""), + 'keys': [ + {'key': "type", 'values': Fw.ExprICMP.values()} + ] + }, + self.STATM_LOG: { + 'name': Fw.Statements.LOG.value, + 'tooltip': QC.translate("firewall", "Print a message when this rule matches a packet."), + 'keys': [ + {'key': Fw.ExprLog.PREFIX.value, 'values': []} + ] + }, + self.STATM_QUOTA: { + 'name': Fw.ExprQuota.QUOTA.value, + 'tooltip': QC.translate("firewall", """ +Apply quotas on connections. + +For example when: + - "quota over 10/mbytes" -> apply the Action defined (DROP) + - "quota until 10/mbytes" -> apply the Action defined (ACCEPT) + +The value must be in the format: VALUE/UNITS, for example: + - 10mbytes, 1/gbytes, etc +"""), + 'keys': [ + {'key': Fw.ExprQuota.OVER.value, 'values': []}, + {'key': Fw.ExprQuota.UNIT.value, 'values': [ + "1/{0}".format(Fw.RateUnits.BYTES.value), + "1/{0}".format(Fw.RateUnits.KBYTES.value), + "1/{0}".format(Fw.RateUnits.MBYTES.value), + "1/{0}".format(Fw.RateUnits.GBYTES.value), + ]} + ] + }, + self.STATM_COUNTER: { + 'name': Fw.ExprCounter.COUNTER.value, + 'tooltip': QC.translate("firewall", ""), + # packets, bytes + 'keys': [ + {'key': Fw.ExprCounter.PACKETS.value, 'values': None}, + {'key': Fw.ExprCounter.NAME.value, 'values': []} + ] + }, + # TODO: https://github.com/evilsocket/opensnitch/wiki/System-rules#rules-expressions + self.STATM_LIMIT: { + 'name': Fw.ExprLimit.LIMIT.value, + 'tooltip': QC.translate("firewall", """ +Apply limits on connections. + +For example when: + - "limit over 10/mbytes/minute" -> apply the Action defined (DROP, ACCEPT, etc) + (When there're more than 10MB per minute, apply an Action) + + - "limit until 10/mbytes/hour" -> apply the Action defined (ACCEPT) + +The value must be in the format: VALUE/UNITS/TIME, for example: + - 10/mbytes/minute, 1/gbytes/hour, etc +"""), + + 'keys': [ + {'key': Fw.ExprLimit.OVER.value, 'values': []}, + {'key': Fw.ExprLimit.UNITS.value, 'values': [ + "1/{0}/{1}".format(Fw.RateUnits.BYTES.value, Fw.TimeUnits.SECOND.value), + "1/{0}/{1}".format(Fw.RateUnits.KBYTES.value, Fw.TimeUnits.MINUTE.value), + "1/{0}/{1}".format(Fw.RateUnits.MBYTES.value, Fw.TimeUnits.HOUR.value), + "1/{0}/{1}".format(Fw.RateUnits.GBYTES.value, Fw.TimeUnits.DAY.value), + ]} + ] + }, + #self.STATM_TCP: { + # 'name': Fw.Statements.TCP.value, # ['dport', 'sport' ... ] + # 'key': Fw.Statements.DADDR.value, + # 'values': [] + #}, + #self.STATM_UDP: { + # 'name': Fw.Statements.UDP.value, + # 'key': Fw.Statements.DADDR.value, # ['dport', 'sport' ... ] + # 'values': [] + #}, + } + + self._notification_callback.connect(self._cb_notification_callback) + self._notifications_sent = {} + + self.uuid = "" + self.simple_port_idx = None + + self._nodes.nodesUpdated.connect(self._cb_nodes_updated) + self.cmdClose.clicked.connect(self._cb_close_clicked) + self.cmdReset.clicked.connect(self._cb_reset_clicked) + self.cmdAdd.clicked.connect(self._cb_add_clicked) + self.cmdSave.clicked.connect(self._cb_save_clicked) + self.cmdDelete.clicked.connect(self._cb_delete_clicked) + self.helpButton.clicked.connect(self._cb_help_button_clicked) + self.comboVerdict.currentIndexChanged.connect(self._cb_verdict_changed) + self.lineVerdictParms.textChanged.connect(self._cb_verdict_parms_changed) + self.checkEnable.toggled.connect(self._cb_check_enable_toggled) + self.lineDescription.textChanged.connect(self._cb_description_changed) + + self.cmdAddStatement.clicked.connect(self._cb_add_new_statement) + self.cmdDelStatement.clicked.connect(self._cb_del_statement) + # remove default page + self.toolBoxSimple.removeItem(0) + self.add_new_statement("", self.toolBoxSimple) + self.hboxAdvanced.setVisible(False) + # setTabVisible not available on <= 5.14 + #self.tabWidget.setTabVisible(0, True) + + if QtGui.QIcon.hasThemeIcon("emblem-default"): + return + + # ----------------------------------------------------------- + + saveIcon = Icons.new(self, "document-save") + closeIcon = Icons.new(self, "window-close") + delIcon = Icons.new(self, "edit-delete") + addIcon = Icons.new(self, "list-add") + remIcon = Icons.new(self, "list-remove") + helpIcon = Icons.new(self, "help-browser") + self.cmdSave.setIcon(saveIcon) + self.cmdDelete.setIcon(delIcon) + self.cmdClose.setIcon(closeIcon) + self.cmdAdd.setIcon(addIcon) + self.helpButton.setIcon(helpIcon) + self.cmdAddStatement.setIcon(addIcon) + self.cmdDelStatement.setIcon(remIcon) + + def show(self): + super(FwRuleDialog, self).show() + self._reset_fields() + + if FwUtils.isProtobufSupported() == False: + self._disable_controls() + self._disable_buttons() + self._set_status_error( + QC.translate( + "firewall", + "Your protobuf version is incompatible, you need to install protobuf 3.8.0 or superior\n(pip3 install --ignore-installed protobuf==3.8.0)" + ) + ) + return False + + self._load_nodes() + self.comboDirection.currentIndexChanged.connect(self._cb_direction_changed) + return True + + def _close(self): + self.comboDirection.currentIndexChanged.disconnect(self._cb_direction_changed) + self.hide() + + @QtCore.pyqtSlot(ui_pb2.NotificationReply) + def _cb_notification_callback(self, reply): + self._enable_buttons() + + try: + if reply.id not in self._notifications_sent: + return + + rep = self._notifications_sent[reply.id] + if reply.code == ui_pb2.OK: + if 'operation' in rep and rep['operation'] == self.OP_DELETE: + self.tabWidget.setDisabled(True) + self._set_status_successful(QC.translate("firewall", "Rule deleted")) + self._disable_controls() + del self._notifications_sent[reply.id] + return + + if 'operation' in rep and rep['operation'] == self.OP_SAVE: + self._set_status_successful(QC.translate("firewall", "Rule saved")) + else: + self._set_status_successful(QC.translate("firewall", "Rule added")) + + else: + # XXX: The errors returned by the nftables lib are not really descriptive. + # "invalid argument", "no such file or directory", without context + # 1st one: invalid combination of table/chain/priorities? + # 2nd one: does the table/chain exist? + errormsg = QC.translate("firewall", "Error adding rules:\n{0}".format(reply.data)) + if 'operation' in rep and rep['operation'] == self.OP_SAVE: + if 'uuid' in rep and rep['uuid'] in reply.data: + errormsg = QC.translate("firewall", "Error saving rule") + else: + self._set_status_message(QC.translate("firewall", "Rule saved, but there're other rules with errors (REVIEW):\n{0}".format(reply.data))) + return + self._set_status_error(errormsg) + + except Exception as e: + print("[fw rule dialog exception] notif error:", e) + finally: + if reply.id in self._notifications_sent: + del self._notifications_sent[reply.id] + + @QtCore.pyqtSlot(int) + def _cb_nodes_updated(self, total): + self.tabWidget.setDisabled(True if total == 0 else False) + + def closeEvent(self, e): + self._close() + + + def _cb_check_enable_toggled(self, status): + self._enable_save() + + def _cb_description_changed(self, text): + self._enable_save() + + def _cb_help_button_clicked(self): + QuickHelp.show( + QC.translate("firewall", + "You can use ',' or '-' to specify multiple ports/IPs or ranges/values:<br><br>" \ + "ports: 22 or 22,443 or 50000-60000<br>" \ + "IPs: 192.168.1.1 or 192.168.1.30-192.168.1.130<br>" \ + "Values: echo-reply,echo-request<br>" \ + "Values: new,established,related" + ) + ) + + + def _cb_close_clicked(self): + self._close() + + def _cb_delete_clicked(self): + node_addr, node, chain, err = self.form_to_protobuf() + if err != None: + self._set_status_error(QC.translate("firewall", "Invalid rule: {0}".format(err))) + return + + self._set_status_message(QC.translate("firewall", "Deleting rule, wait")) + ok, fw_config = self._fw.delete_rule(node_addr, self.uuid) + if not ok: + self._set_status_error(QC.translate("firewall", "Error updating rule")) + return + + if self.comboNodes.currentIndex() == 0: + self.send_notifications(node['firewall'], self.OP_DELETE) + else: + self.send_notification(node_addr, node['firewall'], self.OP_DELETE) + + def _cb_save_clicked(self): + if len(self.statements) == 0: + self._set_status_message(QC.translate("firewall", "Add at least one statement.")) + return + node_addr, node, chain, err = self.form_to_protobuf() + if err != None: + self._set_status_error(QC.translate("firewall", "Invalid rule: {0}".format(err))) + return + + self._set_status_message(QC.translate("firewall", "Adding rule, wait")) + ok, err = self._fw.update_rule(node_addr, self.uuid, chain) + if not ok: + self._set_status_error(QC.translate("firewall", "Error updating rule ({0}): {1}".format(node_addr, err))) + return + + self._enable_buttons(False) + if self.comboNodes.currentIndex() == 0: + self.send_notification(node_addr, node['firewall'], self.OP_SAVE, self.uuid) + else: + self.send_notifications(node['firewall'], self.OP_SAVE) + + def _cb_reset_clicked(self): + self._reset_widgets("", self.toolBoxSimple) + self.add_new_statement(QC.translate("firewall", "<select a statement>"), self.toolBoxSimple) + + def _cb_add_clicked(self): + if len(self.statements) == 0: + self._set_status_message(QC.translate("firewall", "Add at least one statement.")) + return + node_addr, node, chain, err = self.form_to_protobuf() + if err != None: + self._set_status_error(QC.translate("firewall", "Invalid rule: {0}".format(err))) + return + + ok, err = self._fw.insert_rule(node_addr, chain) + if not ok: + self._set_status_error(QC.translate("firewall", "Error adding rule: {0}".format(err))) + return + self._set_status_message(QC.translate("firewall", "Adding rule, wait")) + self._enable_buttons(False) + + if self.comboNodes.currentIndex() == 0: + self.send_notification(node_addr, node['firewall'], self.OP_NEW, chain.Rules[0].UUID) + else: + self.send_notifications(node['firewall'], self.OP_NEW) + + def _cb_add_new_statement(self): + self._enable_save() + self.add_new_statement(QC.translate("firewall", "<select a statement>"), self.toolBoxSimple) + + def _cb_del_statement(self): + self._enable_save() + + idx = self.toolBoxSimple.currentIndex() + if idx < 0: + return + + if idx in self.statements: + del self.statements[idx] + + w = self.toolBoxSimple.widget(idx) + if w != None: + w.setParent(None) + + self._reorder_toolbox_pages() + + def _cb_statem_combo_changed(self, idx): + self._enable_save() + + st_idx = self.toolBoxSimple.currentIndex() + self._configure_statem_value_opts(st_idx) + w = self.statements[st_idx] + tidx = 0 if idx == 0 else idx-1 + w['value'].setToolTip(self.STATM_CONF[tidx]['tooltip']) + self._set_statement_title(st_idx, w['value'].currentText()) + + def _cb_statem_value_changed(self, val): + self._enable_save() + + st_idx = self.toolBoxSimple.currentIndex() + self._set_statement_title(st_idx) + + def _cb_statem_value_index_changed(self, idx): + self._enable_save() + + st_idx = self.toolBoxSimple.currentIndex() + w = self.statements[st_idx] + idx = w['what'].currentIndex()-1 # first item is blank + val = w['value'].currentText().lower() + if idx != -1 and (idx == self.STATM_SPORT or idx == self.STATM_DPORT): + # automagically choose the protocol for the selected port: + # echo/7 (tcp) -> tcp + if Fw.PortProtocols.TCP.value in val: + w['opts'].setCurrentIndex( + Fw.PortProtocols.values().index(Fw.PortProtocols.TCP.value) + ) + elif Fw.PortProtocols.UDP.value in val: + w['opts'].setCurrentIndex( + Fw.PortProtocols.values().index(Fw.PortProtocols.UDP.value) + ) + self._set_statement_title(st_idx) + + def _cb_statem_op_changed(self, idx): + self._enable_save() + + st_idx = self.toolBoxSimple.currentIndex() + self._set_statement_title(st_idx) + + def _cb_statem_opts_changed(self, idx): + self._enable_save() + + st_idx = self.toolBoxSimple.currentIndex() + self._set_statement_title(st_idx) + + def _cb_direction_changed(self, idx): + self._enable_save() + + self._is_valid_rule() + + def _cb_verdict_changed(self, idx): + self._enable_save() + + showVerdictParms = self._has_verdict_parms(idx) + self.lineVerdictParms.setVisible(showVerdictParms) + self.comboVerdictParms.setVisible(showVerdictParms) + self._configure_verdict_parms(idx) + + + def _cb_verdict_parms_changed(self, idx): + self._enable_save() + + def _is_valid_rule(self): + if (self.comboVerdict.currentText().lower() == Config.ACTION_REDIRECT or \ + self.comboVerdict.currentText().lower() == Config.ACTION_TPROXY or \ + self.comboVerdict.currentText().lower() == Config.ACTION_DNAT) and \ + (self.comboDirection.currentIndex() == self.IN or self.comboDirection.currentIndex() == self.POSTROUTING): + self._set_status_message( + QC.translate( + "firewall", + "{0} cannot be used with IN or POSTROUTING directions.".format(self.comboVerdict.currentText().upper()) + ) + ) + return False + elif self.comboVerdict.currentText().lower() == Config.ACTION_SNAT and \ + self.comboDirection.currentIndex() != self.POSTROUTING: + self._set_status_message( + QC.translate( + "firewall", + "{0} can only be used with POSTROUTING.".format(self.comboVerdict.currentText().upper()) + ) + ) + self.comboDirection.setCurrentIndex(self.POSTROUTING) + return False + + self._set_status_message("") + return True + + def _has_verdict_parms(self, idx): + # TODO: + # Fw.Verdicts.values()[idx+1] == Config.ACTION_REJECT or \ + # Fw.Verdicts.values()[idx+1] == Config.ACTION_JUMP or \ + return Fw.Verdicts.values()[idx+1] == Config.ACTION_QUEUE or \ + Fw.Verdicts.values()[idx+1] == Config.ACTION_REDIRECT or \ + Fw.Verdicts.values()[idx+1] == Config.ACTION_TPROXY or \ + Fw.Verdicts.values()[idx+1] == Config.ACTION_DNAT or \ + Fw.Verdicts.values()[idx+1] == Config.ACTION_SNAT or \ + Fw.Verdicts.values()[idx+1] == Config.ACTION_MASQUERADE + + def _configure_verdict_parms(self, idx): + self.comboVerdictParms.clear() + + verdict = Fw.Verdicts.values()[idx+1] + if verdict == Config.ACTION_QUEUE: + self.comboVerdictParms.addItem(QC.translate("firewall", "num"), "num") + + elif verdict == Config.ACTION_JUMP: + self.comboVerdictParms.setVisible(False) + + elif verdict == Config.ACTION_REDIRECT or \ + verdict == Config.ACTION_TPROXY or \ + verdict == Config.ACTION_SNAT or \ + verdict == Config.ACTION_DNAT: + self.comboVerdictParms.addItem(QC.translate("firewall", "to"), "to") + + elif verdict == Config.ACTION_MASQUERADE: + # for persistent,fully-random,etc, options + self.comboVerdictParms.addItem("") + self.comboVerdictParms.addItem(QC.translate("firewall", "to"), "to") + + # https://wiki.nftables.org/wiki-nftables/index.php/Performing_Network_Address_Translation_(NAT)#Redirect + if (verdict == Config.ACTION_REDIRECT or verdict == Config.ACTION_DNAT) and \ + (self.comboDirection.currentIndex() != self.OUT and self.comboDirection.currentIndex() != self.PREROUTING): + self.comboDirection.setCurrentIndex(self.OUT) + + elif self.comboVerdict.currentText().lower() == Config.ACTION_SNAT and \ + self.comboDirection.currentIndex() != self.POSTROUTING: + self.comboDirection.setCurrentIndex(self.POSTROUTING) + + def _reorder_toolbox_pages(self): + tmp = {} + for i,k in enumerate(self.statements): + tmp[i] = self.statements[k] + self.statements = tmp + + def _reset_widgets(self, title, topWidget): + for i in range(topWidget.count()): + topWidget.removeItem(i) + w = topWidget.widget(i) + if w is not None: + w.setParent(None) + + self.statements = {} + self.st_num = 0 + + # if we don't do this, toolbox's subwidgets are not deleted (removed + # from the GUI, but not deleted), so sometimes after loading/closing several rules, + # you may end up with rules mixed on the same layout/form. + self.toolBoxSimple.setParent(None) + self.toolBoxSimple = QtWidgets.QToolBox() + self.tabWidget.widget(0).layout().addWidget(self.toolBoxSimple) + #self.toolBoxSimple.currentChanged.connect(self._cb_toolbox_page_changed) + + def _set_statement_title(self, st_idx, value=None): + """Transform the widgets to nftables rule text format + """ + self._reset_status_message() + self.toolBoxSimple.setItemText(st_idx, "") + w = self.statements[st_idx] + idx = w['what'].currentIndex()-1 # first item is blank + if idx == -1: + return + + st = self.STATM_CONF[idx]['name'] + st_opts = w['opts'].currentText() + if idx == self.STATM_DEST_IP or idx == self.STATM_SOURCE_IP: + st = st_opts + if idx == self.STATM_DPORT or idx == self.STATM_SPORT: + st = st_opts + + title = st + for keys in self.STATM_CONF[idx]['keys']: + title += " " + keys['key'] + st_op = Fw.Operator.values()[w['op'].currentIndex()] + st_val = w['value'].currentText() + if value != None: + st_val = value + + # override previous setup for some statements + if idx == self.STATM_META: + title = "{0} {1} {2} {3}".format(st, st_opts, st_op, st_val) + elif idx == self.STATM_QUOTA: + title = "{0} {1} {2}".format(st, st_opts, st_val) + elif idx == self.STATM_LIMIT: + title = "{0} {1} {2}".format(st, st_opts, st_val) + else: + title = "{0} {1} {2}".format(title, st_op, st_val) + + self.toolBoxSimple.setItemText(st_idx, title) + + def _configure_statem_value_opts(self, st_idx): + w = self.statements[st_idx] + idx = w['what'].currentIndex()-1 # first item is blank + if idx == -1: + return + + w['value'].blockSignals(True); + w['opts'].blockSignals(True); + + oldValue = w['value'].currentText() + w['value'].clear() + for k in self.STATM_CONF[idx]['keys']: + if k['values'] == None: + continue + w['value'].addItems(k['values']) + w['value'].setCurrentText(oldValue) + + w['opts'].clear() + if idx == self.STATM_DPORT or \ + idx == self.STATM_SPORT: + w['op'].setVisible(True) + w['opts'].setVisible(True) + w['opts'].addItems(Fw.PortProtocols.values()) + + elif idx == self.STATM_DEST_IP or \ + idx == self.STATM_SOURCE_IP: + w['op'].setVisible(True) + w['opts'].setVisible(True) + w['opts'].addItems(Fw.Family.values()) + w['opts'].removeItem(0) # remove 'inet' item + + elif idx == self.STATM_IIFNAME or idx == self.STATM_OIFNAME: + w['op'].setVisible(True) + w['opts'].setVisible(False) + if self._nodes.is_local(self.comboNodes.currentText()): + w['value'].addItems(NetworkInterfaces.list().keys()) + w['value'].setCurrentText("") + + elif idx == self.STATM_META: + w['op'].setVisible(True) + w['opts'].setVisible(True) + # exclude first item of the list + w['opts'].addItems(Fw.ExprMeta.values()[1:]) + + elif idx == self.STATM_ICMP or idx == self.STATM_ICMPv6 or \ + idx == self.STATM_CT_STATE or idx == self.STATM_CT_MARK: + w['op'].setVisible(True) + w['opts'].setVisible(False) + + elif idx == self.STATM_LOG: + w['op'].setVisible(False) + w['opts'].setVisible(True) + w['opts'].addItems(Fw.ExprLogLevels.values()) + w['opts'].setCurrentIndex( + # nftables default log level is warn + Fw.ExprLogLevels.values().index(Fw.ExprLogLevels.WARN.value) + ) + elif idx == self.STATM_QUOTA or idx == self.STATM_LIMIT: + w['op'].setVisible(False) + w['opts'].setVisible(True) + w['opts'].addItems([Fw.ExprQuota.OVER.value, Fw.ExprQuota.UNTIL.value]) + else: + w['op'].setVisible(False) + w['opts'].setVisible(False) + + w['opts'].blockSignals(False); + w['value'].blockSignals(False); + + def add_new_statement(self, title="", topWidget=None): + """Creates dynamically the widgets to define firewall rules: + statement (dst port, dst ip, log), protocol, operator (==, !=,...) + and value (443, 1.1.1.1, etc) + Some expressions may have different options (protocol, family, options, etc) + """ + w = QtWidgets.QWidget() + w.setParent(topWidget) + l = QtWidgets.QVBoxLayout(w) + + boxH1 = QtWidgets.QHBoxLayout() + boxH2 = QtWidgets.QHBoxLayout() + l.addLayout(boxH1) + l.addLayout(boxH2) + + # row 1: | statement | protocol | + stWidget = QtWidgets.QComboBox(w) + stWidget.addItems(self.STATM_LIST) + + prots = ["TCP", "UDP", "ICMP"] + stOptsWidget = QtWidgets.QComboBox(w) + stOptsWidget.setSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed) + stOptsWidget.addItems(prots) + + # row 2: | operator | value | + ops = [ + QC.translate("firewall", "Equal"), + QC.translate("firewall", "Not equal"), + QC.translate("firewall", "Greater or equal than"), + QC.translate("firewall", "Greater than"), + QC.translate("firewall", "Less or equal than"), + QC.translate("firewall", "Less than") + ] + stOpWidget = QtWidgets.QComboBox(w) + stOpWidget.setSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed) + stOpWidget.addItems(ops) + + stValueWidget = QtWidgets.QComboBox(w) + stValueWidget.setEditable(True) + stValueWidget.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) + stValueWidget.setCurrentText("") + + # add statement, proto/opts, operator and value + boxH1.addWidget(stWidget) + boxH1.addWidget(stOptsWidget) + boxH2.addWidget(stOpWidget) + boxH2.addWidget(stValueWidget) + w.setLayout(l) + + # insert page after current index + curIdx = self.toolBoxSimple.currentIndex() + topWidget.insertItem(curIdx+1, w, title) + topWidget.setCurrentIndex(curIdx+1) + + # if current index is not the last one, reorder statements + if curIdx+1 != self.st_num: + for i in range(curIdx+1, self.st_num): + if i in self.statements: + self.statements[i+1] = self.statements[i] + + self.statements[curIdx+1] = { + 'what': stWidget, + 'opts': stOptsWidget, + 'op': stOpWidget, + 'value': stValueWidget + } + + stWidget.currentIndexChanged.connect(self._cb_statem_combo_changed) + stOpWidget.currentIndexChanged.connect(self._cb_statem_op_changed) + stOptsWidget.currentIndexChanged.connect(self._cb_statem_opts_changed) + stValueWidget.currentIndexChanged.connect(self._cb_statem_value_index_changed) + stValueWidget.currentTextChanged.connect(self._cb_statem_value_changed) + + self.st_num += 1 + + def _load_nodes(self): + self.comboNodes.clear() + self._node_list = self._nodes.get() + #self.comboNodes.addItem(QC.translate("firewall", "All")) + for addr in self._node_list: + self.comboNodes.addItem(addr) + + if len(self._node_list) == 0: + self.tabWidget.setDisabled(True) + + def _load_meta_statement(self, exp, idx): + try: + isMultiProto = False + isSetMark = False + newStatm = self.STATM_SPORT + newValue = "" + optsValue = "" + for v in exp.Statement.Values: + if v.Key == Fw.ExprMeta.SET.value: + isSetMark = True + continue + if isSetMark and v.Key == Fw.ExprMeta.MARK.value: + newStatm = self.STATM_META_SET_MARK + if self._is_valid_int_value(v.Value): + newValue = v.Value + else: + self._set_status_error( + QC.translate( + "firewall", + "Invalid mark ({0})".format(v.Value) + ) + ) + break + + if v.Key == Fw.ExprMeta.L4PROTO.value: + optsValue = v.Value + if v.Key == Fw.Statements.SPORT.value: + isMultiProto = True + newValue = v.Value + break + elif v.Key == Fw.Statements.DPORT.value: + newStatm = self.STATM_DPORT + isMultiProto = True + newValue = v.Value + break + + if isSetMark: + self.statements[idx]['what'].setCurrentIndex(newStatm+1) + self.statements[idx]['value'].setCurrentText(newValue) + + elif isMultiProto: + self.statements[idx]['what'].setCurrentIndex(newStatm+1) + self.statements[idx]['opts'].setCurrentIndex( + Fw.PortProtocols.values().index(optsValue) + ) + try: + self.statements[idx]['value'].setCurrentIndex( + self.net_srv.index_by_port(newValue) + ) + except: + self.statements[idx]['value'].setCurrentText(newValue) + + else: + self.statements[idx]['what'].setCurrentIndex(self.STATM_META+1) + self.statements[idx]['opts'].setCurrentIndex( + # first item of the list is "set", not present in the combobox + Fw.ExprMeta.values().index(exp.Statement.Values[0].Key)-1 + ) + self.statements[idx]['value'].setCurrentText(exp.Statement.Values[0].Value) + + except Exception as e: + print("_load_meta_statement() exception:", e) + self._set_status_message(e) + + def _load_limit_statement(self, exp, idx): + try: + self.statements[idx]['what'].setCurrentIndex(self.STATM_LIMIT+1) + self.statements[idx]['opts'].setCurrentIndex(1) + lval = "" + for v in exp.Statement.Values: + if v.Key == Fw.ExprLimit.OVER.value: + self.statements[idx]['opts'].setCurrentIndex(0) + elif v.Key == Fw.ExprLimit.UNITS.value: + lval = v.Value + elif v.Key == Fw.ExprLimit.RATE_UNITS.value: + lval = "%s/%s" % (lval, v.Value) + elif v.Key == Fw.ExprLimit.TIME_UNITS.value: + lval = "%s/%s" % (lval, v.Value) + + self.statements[idx]['value'].setCurrentText(lval) + except Exception as e: + print("_load_limit_statement() exception:", e) + self._set_status_message(e) + + def _load_ct_statement(self, exp, idx): + """load CT statements, for example: + Name: ct, Key: set, Key: mark, Value: 123 + Name: ct, Key: mark, Value: 123 + Name: ct, Key: state, value: new,established + """ + try: + if exp.Statement.Values[0].Key == Fw.ExprCt.STATE.value: + self.statements[idx]['what'].setCurrentIndex(self.STATM_CT_STATE+1) + self.statements[idx]['value'].setCurrentText(exp.Statement.Values[0].Value) + for v in exp.Statement.Values: + curText = self.statements[idx]['value'].currentText() + if v.Value not in curText: + self.statements[idx]['value'].setCurrentText( + "{0},{1}".format( + curText, + v.Value + ) + ) + + elif exp.Statement.Values[0].Key == Fw.ExprCt.SET.value: + self.statements[idx]['what'].setCurrentIndex(self.STATM_CT_SET+1) + markVal = "" + for v in exp.Statement.Values: + if v.Key == Fw.ExprCt.MARK.value: + markVal = v.Value + break + + self.statements[idx]['value'].setCurrentText(markVal) + if markVal == "": + raise ValueError( + QC.translate("firewall", "Warning: ct set mark value is empty, malformed rule?") + ) + + elif exp.Statement.Values[0].Key == Fw.ExprCt.MARK.value: + self.statements[idx]['what'].setCurrentIndex(self.STATM_CT_MARK+1) + self.statements[idx]['value'].setCurrentText(exp.Statement.Values[0].Value) + + except Exception as e: + print("_load_ct_statement() exception:", e) + self._set_status_message(e) + + def load(self, addr, uuid): + if not self.show(): + return + + self.FORM_TYPE = self.FORM_TYPE_SIMPLE + self.setWindowTitle(QC.translate("firewall", "Firewall rule")) + self.cmdDelete.setVisible(True) + self.cmdSave.setVisible(True) + self.cmdAdd.setVisible(False) + self.checkEnable.setVisible(True) + self.checkEnable.setEnabled(True) + self.checkEnable.setChecked(True) + self.frameDirection.setVisible(True) + self.comboNodes.setCurrentText(addr) + + self._enable_buttons() + + self.uuid = uuid + + node, rule = self._fw.get_rule_by_uuid(uuid) + if rule == None or \ + (rule.Hook.lower() != Fw.Hooks.INPUT.value and \ + rule.Hook.lower() != Fw.Hooks.FORWARD.value and \ + rule.Hook.lower() != Fw.Hooks.PREROUTING.value and \ + rule.Hook.lower() != Fw.Hooks.POSTROUTING.value and \ + rule.Hook.lower() != Fw.Hooks.OUTPUT.value): + hook = "invalid" if rule == None else rule.Hook + self._set_status_error(QC.translate("firewall", "Rule hook ({0}) not supported yet".format(hook))) + self._disable_controls() + return + + self.checkEnable.setChecked(rule.Rules[0].Enabled) + self.lineDescription.setText(rule.Rules[0].Description) + + self.tabWidget.blockSignals(True); + self.hboxAdvanced.setVisible(True) + self._reset_widgets("", self.toolBoxSimple) + self.tabWidget.setCurrentIndex(0) + + if len(rule.Rules[0].Expressions) <= 1: + self.tabWidget.setTabText(0, QC.translate("firewall", "Simple")) + self.add_new_statement("", self.toolBoxSimple) + else: + for i in enumerate(rule.Rules[0].Expressions): + self.add_new_statement("", self.toolBoxSimple) + self.tabWidget.setTabText(0, QC.translate("firewall", "Advanced")) + + self.tabWidget.blockSignals(False); + + isNotSupported = False + idx = 0 + for exp in rule.Rules[0].Expressions: + #print(idx, "|", exp) + + # set current page, so the title and opts of each statement is + # configured properly. + self.toolBoxSimple.setCurrentIndex(idx) + + if Fw.Utils.isExprPort(exp.Statement.Name): + if exp.Statement.Values[0].Key == Fw.Statements.DPORT.value: + self.statements[idx]['what'].setCurrentIndex(self.STATM_DPORT+1) + elif exp.Statement.Values[0].Key == Fw.Statements.SPORT.value: + self.statements[idx]['what'].setCurrentIndex(self.STATM_SPORT+1) + + try: + self.statements[idx]['value'].setCurrentIndex( + self.net_srv.index_by_port(exp.Statement.Values[0].Value) + ) + except: + self.statements[idx]['value'].setCurrentText(exp.Statement.Values[0].Value) + + st_name = exp.Statement.Name + self.statements[idx]['opts'].setCurrentIndex( + Fw.PortProtocols.values().index(st_name.lower()) + ) + + elif exp.Statement.Name == Fw.Statements.IP.value or exp.Statement.Name == Fw.Statements.IP6.value: + if exp.Statement.Values[0].Key == Fw.Statements.DADDR.value: + self.statements[idx]['what'].setCurrentIndex(self.STATM_DEST_IP+1) + elif exp.Statement.Values[0].Key == Fw.Statements.SADDR.value: + self.statements[idx]['what'].setCurrentIndex(self.STATM_SOURCE_IP+1) + + self.statements[idx]['value'].setCurrentText(exp.Statement.Values[0].Value) + + st_name = exp.Statement.Name + self.statements[idx]['opts'].setCurrentIndex( + Fw.Family.values().index(st_name.lower())-1 # first item does not apply + ) + + elif exp.Statement.Name == Fw.Statements.IIFNAME.value: + self.statements[idx]['what'].setCurrentIndex(self.STATM_IIFNAME+1) + self.statements[idx]['value'].setCurrentText(exp.Statement.Values[0].Key) + + elif exp.Statement.Name == Fw.Statements.OIFNAME.value: + self.statements[idx]['what'].setCurrentIndex(self.STATM_OIFNAME+1) + self.statements[idx]['value'].setCurrentText(exp.Statement.Values[0].Key) + + elif exp.Statement.Name == Fw.Statements.CT.value: + self._load_ct_statement(exp, idx) + + elif exp.Statement.Name == Fw.Statements.META.value: + self._load_meta_statement(exp, idx) + + elif exp.Statement.Name == Fw.Statements.ICMP.value or exp.Statement.Name == Fw.Statements.ICMPv6.value: + if exp.Statement.Name == Fw.Statements.ICMP.value: + self.statements[idx]['what'].setCurrentIndex(self.STATM_ICMP+1) + else: + self.statements[idx]['what'].setCurrentIndex(self.STATM_ICMPv6+1) + + self.statements[idx]['value'].setCurrentText(exp.Statement.Values[0].Value) + for v in exp.Statement.Values: + curText = self.statements[idx]['value'].currentText() + if v.Value not in curText: + self.statements[idx]['value'].setCurrentText( + "{0},{1}".format( + curText, + v.Value + ) + ) + + elif exp.Statement.Name == Fw.Statements.LOG.value: + self.statements[idx]['what'].setCurrentIndex(self.STATM_LOG+1) + + for v in exp.Statement.Values: + if v.Key == Fw.ExprLog.PREFIX.value: + self.statements[idx]['value'].setCurrentText(v.Value) + elif v.Key == Fw.ExprLog.LEVEL.value: + try: + lvl = Fw.ExprLogLevels.values().index(v.Value) + except: + lvl = Fw.ExprLogLevels.values().index(Fw.ExprLogLevels.WARN.value) + self.statements[idx]['opts'].setCurrentIndex(lvl) + + elif exp.Statement.Name == Fw.Statements.QUOTA.value: + self.statements[idx]['what'].setCurrentIndex(self.STATM_QUOTA+1) + self.statements[idx]['opts'].setCurrentIndex(1) + for v in exp.Statement.Values: + if v.Key == Fw.ExprQuota.OVER.value: + self.statements[idx]['opts'].setCurrentIndex(0) + else: + self.statements[idx]['value'].setCurrentText( + "{0}/{1}".format(v.Value, v.Key) + ) + + elif exp.Statement.Name == Fw.Statements.LIMIT.value: + self._load_limit_statement(exp, idx) + + elif exp.Statement.Name == Fw.Statements.COUNTER.value: + self.statements[idx]['what'].setCurrentIndex(self.STATM_COUNTER+1) + for v in exp.Statement.Values: + if v.Key == Fw.ExprCounter.NAME.value: + self.statements[idx]['value'].setCurrentText(v.Value) + + else: + isNotSupported = True + break + + # a statement may not have an operator. It's assumed that it's the + # equal operator. + op = Fw.Operator.EQUAL.value if exp.Statement.Op == "" else exp.Statement.Op + self.statements[idx]['op'].setCurrentIndex( + Fw.Operator.values().index(op) + ) + + idx+=1 + + if isNotSupported: + self._set_status_error(QC.translate("firewall", "This rule is not supported yet.")) + self._disable_controls() + return + + if rule.Hook.lower() == Fw.Hooks.INPUT.value: + self.comboDirection.setCurrentIndex(self.IN) + elif rule.Hook.lower() == Fw.Hooks.OUTPUT.value: + self.comboDirection.setCurrentIndex(self.OUT) + elif rule.Hook.lower() == Fw.Hooks.FORWARD.value: + self.comboDirection.setCurrentIndex(self.FORWARD) + elif rule.Hook.lower() == Fw.Hooks.PREROUTING.value: + self.comboDirection.setCurrentIndex(self.PREROUTING) + elif rule.Hook.lower() == Fw.Hooks.POSTROUTING.value: + self.comboDirection.setCurrentIndex(self.POSTROUTING) + # TODO: changing the direction of an existed rule needs work, it causes + # some nasty effects. Disabled for now. + self.comboDirection.setEnabled(False) + + try: + self.comboVerdict.setCurrentIndex( + Fw.Verdicts.values().index( + rule.Rules[0].Target.lower() + )-1 + ) + if self._has_verdict_parms(self.comboVerdict.currentIndex()): + tparms = rule.Rules[0].TargetParameters.lower() + parts = tparms.split(" ") + self.lineVerdictParms.setText(parts[1]) + if parts[1] == "": + print("Firewall Rule: verdict parms error:", parts) + except: + self._set_status_error(QC.translate("firewall", "Rule target ({0}) not supported yet".format(rule.Rules[0].Target.lower()))) + self._disable_controls() + + self._enable_save(False) + + def new(self): + if not self.show(): + return + + self._reset_widgets("", self.toolBoxSimple) + self.FORM_TYPE = self.FORM_TYPE_SIMPLE + self.setWindowTitle(QC.translate("firewall", "Firewall rule")) + self.cmdDelete.setVisible(False) + self.cmdSave.setVisible(False) + self.cmdAdd.setVisible(True) + self.checkEnable.setVisible(True) + self.checkEnable.setEnabled(True) + self.checkEnable.setChecked(True) + self.frameDirection.setVisible(True) + + self.cmdSave.setVisible(False) + self.cmdDelete.setVisible(False) + self.cmdAdd.setVisible(True) + + self.hboxAdvanced.setVisible(True) + self.tabWidget.setTabText(0, "") + self.tabWidget.setCurrentIndex(0) + self.add_new_statement("", self.toolBoxSimple) + + def exclude_service(self, direction): + if not self.show(): + return + + self._reset_widgets("", self.toolBoxSimple) + self.setWindowTitle(QC.translate("firewall", "Exclude service")) + self.cmdDelete.setVisible(False) + self.cmdSave.setVisible(False) + self.cmdReset.setVisible(False) + self.cmdAdd.setVisible(True) + self.checkEnable.setVisible(False) + self.checkEnable.setEnabled(True) + self.tabWidget.setTabText(0, "") + self.hboxAdvanced.setVisible(False) + + dirPort = self.STATM_DPORT+1 + self.FORM_TYPE = self.FORM_TYPE_ALLOW_IN_SERVICE + self.lblExcludeTip.setText(QC.translate("firewall", "Allow inbound connections to the selected port.")) + if direction == self.OUT: + self.lblExcludeTip.setText(QC.translate("firewall", "Allow outbound connections to the selected port.")) + self.FORM_TYPE = self.FORM_TYPE_EXCLUDE_SERVICE + dirPort = self.STATM_DPORT+1 + + self.add_new_statement("", self.toolBoxSimple) + self.statements[0]['what'].setCurrentIndex(dirPort) + self.statements[0]['what'].setVisible(False) + self.statements[0]['op'].setVisible(False) + self.statements[0]['value'].setCurrentText("") + + self.frameDirection.setVisible(False) + self.lblExcludeTip.setVisible(True) + + self.checkEnable.setChecked(True) + + def form_to_protobuf(self): + """Transform form widgets to protobuf struct + """ + chain = Fw.ChainFilter.input() + # XXX: tproxy does not work with destnat+output + if self.comboDirection.currentIndex() == self.OUT and \ + (self.comboVerdict.currentIndex()+1 == Fw.Verdicts.values().index(Config.ACTION_TPROXY) or \ + self.comboVerdict.currentIndex()+1 == Fw.Verdicts.values().index(Config.ACTION_DNAT) or \ + self.comboVerdict.currentIndex()+1 == Fw.Verdicts.values().index(Config.ACTION_REDIRECT) + ): + chain = Fw.ChainDstNAT.output() + elif self.comboDirection.currentIndex() == self.FORWARD: + chain = Fw.ChainMangle.forward() + elif self.comboDirection.currentIndex() == self.PREROUTING: + chain = Fw.ChainDstNAT.prerouting() + elif self.comboDirection.currentIndex() == self.POSTROUTING: + chain = Fw.ChainDstNAT.postrouting() + + elif self.comboDirection.currentIndex() == self.OUT or self.FORM_TYPE == self.FORM_TYPE_EXCLUDE_SERVICE: + chain = Fw.ChainMangle.output() + elif self.comboDirection.currentIndex() == self.IN or self.FORM_TYPE == self.FORM_TYPE_ALLOW_IN_SERVICE: + chain = Fw.ChainFilter.input() + + verdict_idx = self.comboVerdict.currentIndex() + verdict = Fw.Verdicts.values()[verdict_idx+1] # index 0 is "" + _target_parms = "" + if self._has_verdict_parms(verdict_idx): + if self.lineVerdictParms.text() == "": + return None, None, None, QC.translate("firewall", "Verdict ({0}) parameters cannot be empty.".format(verdict)) + + # these verdicts parameters need ":" to specify a port or ip:port + if (self.comboVerdict.currentText().lower() == Config.ACTION_REDIRECT or \ + self.comboVerdict.currentText().lower() == Config.ACTION_TPROXY or \ + self.comboVerdict.currentText().lower() == Config.ACTION_SNAT or \ + self.comboVerdict.currentText().lower() == Config.ACTION_DNAT) and \ + ":" not in self.lineVerdictParms.text(): + return None, None, None, QC.translate("firewall", "Verdict ({0}) parameters format is: <IP>:port.".format(verdict)) + + if self.comboVerdict.currentText().lower() == Config.ACTION_QUEUE: + try: + t = int(self.lineVerdictParms.text()) + except: + return None, None, None, QC.translate("firewall", "Verdict ({0}) parameters format must be a number".format(verdict)) + + vidx = self.comboVerdictParms.currentIndex() + _target_parms = "{0} {1}".format( + self.comboVerdictParms.itemData(vidx), + self.lineVerdictParms.text().replace(" ", "") + ) + + rule = Fw.Rules.new( + enabled=self.checkEnable.isChecked(), + _uuid=self.uuid, + description=self.lineDescription.text(), + target=verdict, + target_parms=_target_parms + ) + + for k in self.statements: + st_idx = self.statements[k]['what'].currentIndex()-1 + if st_idx == -1: + return None, None, None, QC.translate("firewall", "select a statement.") + + statement = self.STATM_CONF[st_idx]['name'] + statem_keys = self.STATM_CONF[st_idx]['keys'] + statem_op = Fw.Operator.values()[self.statements[k]['op'].currentIndex()] + statem_opts = self.statements[k]['opts'].currentText().lower() + + key_values = [] + for sk in statem_keys: + if sk['values'] == None: + key_values.append((sk['key'], "")) + else: + statem_value = self.statements[k]['value'].currentText() + val_idx = self.statements[k]['value'].currentIndex() + + if statem_value == "" or (statem_value == "0" and st_idx != self.STATM_META): + return None, None, None, QC.translate("firewall", "value cannot be 0 or empty.") + + if st_idx == self.STATM_QUOTA: + if sk['key'] == Fw.ExprQuota.OVER.value: + if self.statements[k]['opts'].currentIndex() == 0: + key_values.append((sk['key'], "")) + continue + elif sk['key'] == Fw.ExprQuota.UNIT.value or sk['key'] in Fw.RateUnits.values(): + units = statem_value.split("/") + if len(units) != 2: # we expect the format key/value + return None, None, None, QC.translate("firewall", "the value format is 1024/kbytes (or bytes, mbytes, gbytes)") + if units[1] not in Fw.RateUnits.values(): + return None, None, None, QC.translate("firewall", "the value format is 1024/kbytes (or bytes, mbytes, gbytes)") + + sk['key'] = units[1] + statem_value = units[0] + if not self._is_valid_int_value(statem_value): + raise ValueError("quota value is invalid ({0}). It must be value/unit (1/kbytes)".format(statem_value)) + + elif st_idx == self.STATM_LIMIT: + if sk['key'] == Fw.ExprLimit.OVER.value: + if self.statements[k]['opts'].currentIndex() == 0: + key_values.append((sk['key'], "")) + elif sk['key'] == Fw.ExprLimit.UNITS.value: + units = statem_value.split("/") + if len(units) != 3: # we expect the format key/value + return None, None, None, QC.translate("firewall", "the value format is 1024/kbytes/second (or bytes, mbytes, gbytes)") + + if units[1] not in Fw.RateUnits.values(): + return None, None, None, QC.translate("firewall", "rate-limit not valid, use: bytes, kbytes, mbytes or gbytes.") + if units[2] not in Fw.TimeUnits.values(): + return None, None, None, QC.translate("firewall", "time-limit not valid, use: second, minute, hour or day") + key_values.append((Fw.ExprLimit.UNITS.value, units[0])) + key_values.append((Fw.ExprLimit.RATE_UNITS.value, units[1])) + key_values.append((Fw.ExprLimit.TIME_UNITS.value, units[2])) + + continue + + elif st_idx == self.STATM_LOG: + key_values.append((Fw.ExprLog.LEVEL.value, statem_opts)) + + elif st_idx == self.STATM_META: + sk['key'] = self.statements[k]['opts'].currentText() + + elif st_idx == self.STATM_IIFNAME or st_idx == self.STATM_OIFNAME: + # for these statements, the values is set in the Key + # field instead of Value. Value must be empty + sk['key'] = statem_value + statem_value = "" + + elif st_idx == self.STATM_DEST_IP or st_idx == self.STATM_SOURCE_IP: + statement = statem_opts + # convert network u.x.y.z/nn to 1.2.3.4-1.255.255.255 + # format. + # FIXME: This should be supported by the daemon, + # instead of converting it here. + # TODO: validate IP ranges. + if "/" in statem_value: + try: + net = ipaddress.ip_network(statem_value) + hosts = list(net) + statem_value = "{0}-{1}".format(str(hosts[0]), str(hosts[-1])) + except Exception as e: + return None, None, None, QC.translate("firewall", "IP network format error, {0}".format(e)) + elif not "-" in statem_value: + try: + ipaddress.ip_address(statem_value) + except Exception as e: + return None, None, None, QC.translate("firewall", "{0}".format(e)) + + elif st_idx == self.STATM_DPORT or st_idx == self.STATM_SPORT: + # if it's a tcp+udp port, we need to add a meta+l4proto + # statement, with the protos + ports as values. + optsIdx = self.statements[k]['opts'].currentIndex() + isMultiProto = optsIdx == 0 + if isMultiProto: + meta = self.STATM_CONF[self.STATM_META]['keys'][1] + statement = self.STATM_CONF[self.STATM_META]['name'] + # key: l4proto + key_values.append((meta['key'], statem_opts)) + + else: + statement = statem_opts + + # 1. if the value is one of the /etc/services return + # the port + # 2. if the value contains , or - just use the written + # value, to allow multiple ports and ranges. + # 3. otherwise validate that the entered value is an + # int + try: + if "," in statem_value or "-" in statem_value: + raise ValueError("port entered is multiport or a port range") + service_idx = self.net_srv.service_by_name(statem_value) + statem_value = self.net_srv.port_by_index(service_idx) + except: + if "," not in statem_value and "-" not in statem_value: + if not self._is_valid_int_value(statem_value): + return None, None, None, QC.translate("firewall", "port not valid.") + + elif st_idx == self.STATM_CT_SET or st_idx == self.STATM_CT_MARK or st_idx == self.STATM_META_SET_MARK: + if not self._is_valid_int_value(statem_value): + return None, None, None, QC.translate("firewall", "Invalid value {0}, number expected.".format(statem_value)) + + elif st_idx == self.STATM_ICMP or st_idx == self.STATM_ICMPv6: + values = statem_value.split(",") + for val in values: + if val not in Fw.ExprICMP.values(): + return None, None, None, QC.translate("firewall", "Invalid ICMP type \"{0}\".".format(val)) + + keyVal = (sk['key'], statem_value.replace(" ", "")) + if keyVal not in key_values: + key_values.append(keyVal) + else: + print("[REVIEW] statement values duplicated (there shouldn't be):", keyVal) + + exprs = Fw.Expr.new( + statem_op, + statement, + key_values, + ) + rule.Expressions.extend([exprs]) + chain.Rules.extend([rule]) + + node_addr = self.comboNodes.currentText() + node = self._nodes.get_node(node_addr) + return node_addr, node, chain, None + + def _is_valid_int_value(self, value): + try: + int(value) + except: + return False + + return True + + def send_notification(self, node_addr, fw_config, op, uuid): + nid, notif = self._nodes.reload_fw(node_addr, fw_config, self._notification_callback) + self._notifications_sent[nid] = {'addr': node_addr, 'operation': op, 'notif': notif, 'uuid': uuid} + + def send_notifications(self, fw_config, op): + for addr in self._nodes.get_nodes(): + nid, notif = self._nodes.reload_fw(addr, fw_config, self._notification_callback) + self._notifications_sent[nid] = {'addr': addr, 'operation': op, 'notif': notif} + + def _set_status_error(self, msg): + self.statusLabel.show() + self.statusLabel.setStyleSheet('color: red') + self.statusLabel.setText(msg) + + def _set_status_successful(self, msg): + self.statusLabel.show() + self.statusLabel.setStyleSheet('color: green') + self.statusLabel.setText(msg) + + def _set_status_message(self, msg): + self.statusLabel.show() + self.statusLabel.setStyleSheet('color: darkorange') + self.statusLabel.setText(msg) + + def _reset_status_message(self): + self.statusLabel.setText("") + self.statusLabel.hide() + + def _reset_fields(self): + self.FORM_TYPE = self.FORM_TYPE_SIMPLE + self.setWindowTitle(QC.translate("firewall", "Firewall rule")) + + self.cmdDelete.setVisible(False) + self.cmdSave.setVisible(False) + self.cmdAdd.setVisible(True) + + self.checkEnable.setVisible(True) + self.checkEnable.setEnabled(True) + self.checkEnable.setChecked(True) + self.frameDirection.setVisible(True) + self.lblExcludeTip.setVisible(False) + self.lblExcludeTip.setText("") + + self._reset_status_message() + self._enable_buttons() + self.tabWidget.setDisabled(False) + self.lineDescription.setText("") + self.comboDirection.setCurrentIndex(self.IN) + self.comboDirection.setEnabled(True) + + self.comboVerdict.blockSignals(True); + self.comboVerdict.setCurrentIndex(0) + self.comboVerdict.blockSignals(False); + self.lineVerdictParms.setVisible(False) + self.comboVerdictParms.setVisible(False) + self.lineVerdictParms.setText("") + + self.uuid = "" + + def _enable_save(self, enable=True): + """Enable Save buton whenever some detail of a route changes. + The button may or not be hidden. If we're editing a rule it'll be shown + but disabled/enabled. + """ + self.cmdSave.setEnabled(enable) + + def _enable_buttons(self, enable=True): + """Disable add/save buttons until a response is received from the daemon. + """ + self.cmdSave.setEnabled(enable) + self.cmdAdd.setEnabled(enable) + self.cmdDelete.setEnabled(enable) + + def _disable_buttons(self, disabled=True): + self.cmdSave.setDisabled(disabled) + self.cmdAdd.setDisabled(disabled) + self.cmdDelete.setDisabled(disabled) + + def _disable_controls(self): + self._disable_buttons() + self.tabWidget.setDisabled(True) diff --git a/ui/opensnitch/dialogs/preferences.py b/ui/opensnitch/dialogs/preferences.py new file mode 100644 index 0000000..bcdf176 --- /dev/null +++ b/ui/opensnitch/dialogs/preferences.py @@ -0,0 +1,1049 @@ +import sys +import time +import os +import json +import stat + +from PyQt5 import QtCore, QtGui, uic, QtWidgets +from PyQt5.QtCore import QCoreApplication as QC + +from opensnitch.config import Config +from opensnitch.nodes import Nodes +from opensnitch.database import Database +from opensnitch.utils import Message, QuickHelp, Themes, Icons, languages +from opensnitch.utils.xdg import Autostart +from opensnitch.notifications import DesktopNotifications + +from opensnitch import auth +import opensnitch.proto as proto +ui_pb2, ui_pb2_grpc = proto.import_() + +DIALOG_UI_PATH = "%s/../res/preferences.ui" % os.path.dirname(sys.modules[__name__].__file__) +class PreferencesDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]): + + LOG_TAG = "[Preferences] " + _notification_callback = QtCore.pyqtSignal(ui_pb2.NotificationReply) + saved = QtCore.pyqtSignal() + + TAB_POPUPS = 0 + TAB_UI = 1 + TAB_RULES = 2 + TAB_NODES = 3 + TAB_DB = 4 + + NODE_PAGE_GENERAL = 0 + NODE_PAGE_LOGGING = 1 + NODE_PAGE_AUTH = 2 + + SUM = 1 + REST = 0 + + AUTH_SIMPLE = 0 + AUTH_TLS_SIMPLE = 1 + AUTH_TLS_MUTUAL = 2 + + NODE_AUTH = { + AUTH_SIMPLE: auth.Simple, + AUTH_TLS_SIMPLE: auth.TLSSimple, + AUTH_TLS_MUTUAL: auth.TLSMutual + } + NODE_AUTH_VERIFY = { + 0: auth.NO_CLIENT_CERT, + 1: auth.REQ_CERT, + 2: auth.REQ_ANY_CERT, + 3: auth.VERIFY_CERT, + 4: auth.REQ_AND_VERIFY_CERT + } + + def __init__(self, parent=None, appicon=None): + QtWidgets.QDialog.__init__(self, parent, QtCore.Qt.WindowStaysOnTopHint) + + self._themes = Themes.instance() + self._saved_theme = "" + self._restart_msg = QC.translate("preferences", "Restart the GUI in order changes to take effect") + self._changes_needs_restart = None + + self._cfg = Config.get() + self._nodes = Nodes.instance() + self._db = Database.instance() + self._autostart = Autostart() + + self._notification_callback.connect(self._cb_notification_callback) + self._notifications_sent = {} + self._desktop_notifications = DesktopNotifications() + + self.setupUi(self) + self.setWindowIcon(appicon) + + self.checkDBMaxDays.setEnabled(True) + self.dbFileButton.setVisible(False) + self.dbLabel.setVisible(False) + self.dbType = None + + intValidator = QtGui.QDoubleValidator(0, 20, 2, self) + self.lineUIScreenFactor.setValidator(intValidator) + + self.acceptButton.clicked.connect(self._cb_accept_button_clicked) + self.applyButton.clicked.connect(self._cb_apply_button_clicked) + self.cancelButton.clicked.connect(self._cb_cancel_button_clicked) + self.helpButton.clicked.connect(self._cb_help_button_clicked) + self.popupsCheck.clicked.connect(self._cb_popups_check_toggled) + self.dbFileButton.clicked.connect(self._cb_file_db_clicked) + self.cmdTimeoutUp.clicked.connect(lambda: self._cb_cmd_spin_clicked(self.spinUITimeout, self.SUM)) + self.cmdTimeoutDown.clicked.connect(lambda: self._cb_cmd_spin_clicked(self.spinUITimeout, self.REST)) + self.cmdRefreshUIUp.clicked.connect(lambda: self._cb_cmd_spin_clicked(self.spinUIRefresh, self.SUM)) + self.cmdRefreshUIDown.clicked.connect(lambda: self._cb_cmd_spin_clicked(self.spinUIRefresh, self.REST)) + self.cmdUIDensityUp.clicked.connect(lambda: self._cb_cmd_spin_clicked(self.spinUIDensity, self.SUM)) + self.cmdUIDensityDown.clicked.connect(lambda: self._cb_cmd_spin_clicked(self.spinUIDensity, self.REST)) + self.cmdDBMaxDaysUp.clicked.connect(lambda: self._cb_cmd_spin_clicked(self.spinDBMaxDays, self.SUM)) + self.cmdDBMaxDaysDown.clicked.connect(lambda: self._cb_cmd_spin_clicked(self.spinDBMaxDays, self.REST)) + self.cmdDBPurgesUp.clicked.connect(lambda: self._cb_cmd_spin_clicked(self.spinDBPurgeInterval, self.SUM)) + self.cmdDBPurgesDown.clicked.connect(lambda: self._cb_cmd_spin_clicked(self.spinDBPurgeInterval, self.REST)) + self.cmdTestNotifs.clicked.connect(self._cb_test_notifs_clicked) + self.radioSysNotifs.clicked.connect(self._cb_radio_system_notifications) + self.helpButton.setToolTipDuration(30 * 1000) + + self.comboAuthType.currentIndexChanged.connect(self._cb_combo_auth_type_changed) + self.comboAuthType.setItemData(PreferencesDialog.AUTH_SIMPLE, auth.Simple) + self.comboAuthType.setItemData(PreferencesDialog.AUTH_TLS_SIMPLE, auth.TLSSimple) + self.comboAuthType.setItemData(PreferencesDialog.AUTH_TLS_MUTUAL, auth.TLSMutual) + self.comboNodeAuthType.setItemData(PreferencesDialog.AUTH_SIMPLE, auth.Simple) + self.comboNodeAuthType.setItemData(PreferencesDialog.AUTH_TLS_SIMPLE, auth.TLSSimple) + self.comboNodeAuthType.setItemData(PreferencesDialog.AUTH_TLS_MUTUAL, auth.TLSMutual) + self.comboNodeAuthVerifyType.setItemData(0, auth.NO_CLIENT_CERT) + self.comboNodeAuthVerifyType.setItemData(1, auth.REQ_CERT) + self.comboNodeAuthVerifyType.setItemData(2, auth.REQ_ANY_CERT) + self.comboNodeAuthVerifyType.setItemData(3, auth.VERIFY_CERT) + self.comboNodeAuthVerifyType.setItemData(4, auth.REQ_AND_VERIFY_CERT) + + self.comboUIRules.currentIndexChanged.connect(self._cb_combo_uirules_changed) + + if QtGui.QIcon.hasThemeIcon("emblem-default"): + return + + saveIcon = Icons.new(self, "document-save") + applyIcon = Icons.new(self, "emblem-default") + delIcon = Icons.new(self, "edit-delete") + closeIcon = Icons.new(self, "window-close") + openIcon = Icons.new(self, "document-open") + helpIcon = Icons.new(self, "help-browser") + addIcon = Icons.new(self, "list-add") + delIcon = Icons.new(self, "list-remove") + allowIcon = Icons.new(self, "emblem-default") + denyIcon = Icons.new(self, "emblem-important") + rejectIcon = Icons.new(self, "window-close") + self.applyButton.setIcon(applyIcon) + self.cancelButton.setIcon(closeIcon) + self.acceptButton.setIcon(saveIcon) + self.helpButton.setIcon(helpIcon) + self.dbFileButton.setIcon(openIcon) + + self.cmdTimeoutUp.setIcon(addIcon) + self.cmdTimeoutDown.setIcon(delIcon) + self.cmdRefreshUIUp.setIcon(addIcon) + self.cmdRefreshUIDown.setIcon(delIcon) + self.cmdUIDensityUp.setIcon(addIcon) + self.cmdUIDensityDown.setIcon(delIcon) + self.cmdDBMaxDaysUp.setIcon(addIcon) + self.cmdDBMaxDaysDown.setIcon(delIcon) + self.cmdDBPurgesUp.setIcon(addIcon) + self.cmdDBPurgesDown.setIcon(delIcon) + + self.comboUIAction.setItemIcon(Config.ACTION_DENY_IDX, denyIcon) + self.comboUIAction.setItemIcon(Config.ACTION_ALLOW_IDX, allowIcon) + self.comboUIAction.setItemIcon(Config.ACTION_REJECT_IDX, rejectIcon) + + def showEvent(self, event): + super(PreferencesDialog, self).showEvent(event) + + try: + self._changes_needs_restart = None + self._settingsSaved = False + self._reset_status_message() + self._hide_status_label() + self.comboNodes.clear() + + self._load_langs() + + self.comboNodeAddress.clear() + run_path = "/run/user/{0}/opensnitch/".format(os.getuid()) + var_run_path = "/var{0}".format(run_path) + self.comboNodeAddress.addItem("unix:///tmp/osui.sock") + if os.path.exists(run_path): + self.comboNodeAddress.addItem("unix://%s/osui.sock" % run_path) + if os.path.exists(var_run_path): + self.comboNodeAddress.addItem("unix://%s/osui.sock" % var_run_path) + + self._node_list = self._nodes.get() + for addr in self._node_list: + self.comboNodes.addItem(addr) + + if len(self._node_list) == 0: + self._reset_node_settings() + self._set_status_message(QC.translate("preferences", "There're no nodes connected")) + except Exception as e: + print(self.LOG_TAG + "exception loading nodes:", e) + + self._load_settings() + + # connect the signals after loading settings, to avoid firing + # the signals + self.comboNodes.currentIndexChanged.connect(self._cb_node_combo_changed) + self.comboNodeAction.currentIndexChanged.connect(self._cb_node_needs_update) + self.comboNodeDuration.currentIndexChanged.connect(self._cb_node_needs_update) + self.comboNodeMonitorMethod.currentIndexChanged.connect(self._cb_node_needs_update) + self.comboNodeLogLevel.currentIndexChanged.connect(self._cb_node_needs_update) + self.comboNodeLogFile.currentIndexChanged.connect(self._cb_node_needs_update) + self.checkNodeLogUTC.clicked.connect(self._cb_node_needs_update) + self.checkNodeLogMicro.clicked.connect(self._cb_node_needs_update) + self.comboNodeAddress.currentTextChanged.connect(self._cb_node_needs_update) + self.checkInterceptUnknown.clicked.connect(self._cb_node_needs_update) + self.checkApplyToNodes.clicked.connect(self._cb_node_needs_update) + self.comboNodeAction.currentIndexChanged.connect(self._cb_node_needs_update) + self.checkNodeAuthSkipVerify.clicked.connect(self._cb_node_needs_update) + self.comboNodeAuthVerifyType.currentIndexChanged.connect(self._cb_node_needs_update) + + self.comboAuthType.currentIndexChanged.connect(self._cb_combo_auth_type_changed) + self.comboNodeAuthType.currentIndexChanged.connect(self._cb_combo_node_auth_type_changed) + + self.lineCACertFile.textChanged.connect(self._cb_line_certs_changed) + self.lineCertFile.textChanged.connect(self._cb_line_certs_changed) + self.lineCertKeyFile.textChanged.connect(self._cb_line_certs_changed) + self.lineNodeCACertFile.textChanged.connect(self._cb_node_line_certs_changed) + self.lineNodeCertFile.textChanged.connect(self._cb_node_line_certs_changed) + self.lineNodeCertKeyFile.textChanged.connect(self._cb_node_line_certs_changed) + + self.lineUIScreenFactor.textChanged.connect(self._cb_ui_screen_factor_changed) + self.checkUIRules.toggled.connect(self._cb_ui_check_rules_toggled) + self.checkUIAutoScreen.toggled.connect(self._cb_ui_check_auto_scale_toggled) + self.comboUITheme.currentIndexChanged.connect(self._cb_combo_themes_changed) + self.spinUIDensity.valueChanged.connect(self._cb_spin_uidensity_changed) + + self.comboDBType.currentIndexChanged.connect(self._cb_db_type_changed) + self.checkDBMaxDays.toggled.connect(self._cb_db_max_days_toggled) + self.checkDBJrnlWal.toggled.connect(self._cb_db_jrnl_wal_toggled) + + # True when any node option changes + self._node_needs_update = False + + def show_node_prefs(self, addr): + self.show() + self.comboNodes.setCurrentText(addr) + self.tabWidget.setCurrentIndex(self.TAB_NODES) + + def _load_langs(self): + try: + self.comboUILang.clear() + self.comboUILang.blockSignals(True) + self.comboUILang.addItem(QC.translate("preferences", "System default"), "") + langs, langNames = languages.get_all() + for idx, lang in enumerate(langs): + self.comboUILang.addItem(langNames[idx].capitalize(), langs[idx]) + self.comboUILang.blockSignals(False) + except Exception as e: + print(self.LOG_TAG + "exception loading languages:", e) + + def _load_themes(self): + self.comboUITheme.blockSignals(True) + theme_idx, self._saved_theme, theme_density = self._themes.get_saved_theme() + + self.labelThemeError.setVisible(False) + self.labelThemeError.setText("") + self.comboUITheme.clear() + self.comboUITheme.addItem(QC.translate("preferences", "System")) + if self._themes.available(): + themes = self._themes.list_themes() + self.comboUITheme.addItems(themes) + else: + self._saved_theme = "" + self.labelThemeError.setStyleSheet('color: red') + self.labelThemeError.setVisible(True) + self.labelThemeError.setText(QC.translate("preferences", "Themes not available. Install qt-material: pip3 install qt-material")) + + self.comboUITheme.setCurrentIndex(theme_idx) + self._show_ui_density_widgets(theme_idx) + try: + self.spinUIDensity.setValue(int(theme_density)) + except Exception as e: + print("load_theme() invalid theme density scale:", theme_density, ":", e) + + self.comboUITheme.blockSignals(False) + + def _load_settings(self): + self._default_action = self._cfg.getInt(self._cfg.DEFAULT_ACTION_KEY) + self._default_target = self._cfg.getInt(self._cfg.DEFAULT_TARGET_KEY, 0) + self._default_timeout = self._cfg.getInt(self._cfg.DEFAULT_TIMEOUT_KEY, Config.DEFAULT_TIMEOUT) + self._disable_popups = self._cfg.getBool(self._cfg.DEFAULT_DISABLE_POPUPS) + + if self._cfg.hasKey(self._cfg.DEFAULT_DURATION_KEY): + self._default_duration = self._cfg.getInt(self._cfg.DEFAULT_DURATION_KEY) + else: + self._default_duration = self._cfg.DEFAULT_DURATION_IDX + + self.comboUIDuration.setCurrentIndex(self._default_duration) + self.comboUIDialogPos.setCurrentIndex(self._cfg.getInt(self._cfg.DEFAULT_POPUP_POSITION)) + self.comboUIAction.setCurrentIndex(self._default_action) + self.comboUITarget.setCurrentIndex(self._default_target) + self.spinUITimeout.setValue(self._default_timeout) + self.spinUITimeout.setEnabled(not self._disable_popups) + self.popupsCheck.setChecked(self._disable_popups) + + self.showAdvancedCheck.setChecked(self._cfg.getBool(self._cfg.DEFAULT_POPUP_ADVANCED)) + self.dstIPCheck.setChecked(self._cfg.getBool(self._cfg.DEFAULT_POPUP_ADVANCED_DSTIP)) + self.dstPortCheck.setChecked(self._cfg.getBool(self._cfg.DEFAULT_POPUP_ADVANCED_DSTPORT)) + self.uidCheck.setChecked(self._cfg.getBool(self._cfg.DEFAULT_POPUP_ADVANCED_UID)) + + maxmsgsize = self._cfg.getSettings(Config.DEFAULT_SERVER_MAX_MESSAGE_LENGTH) + if maxmsgsize: + self.comboGrpcMsgSize.setCurrentText(maxmsgsize) + else: + self.comboGrpcMsgSize.setCurrentIndex(0) + + self.lineCACertFile.setText(self._cfg.getSettings(Config.AUTH_CA_CERT)) + self.lineCertFile.setText(self._cfg.getSettings(Config.AUTH_CERT)) + self.lineCertKeyFile.setText(self._cfg.getSettings(Config.AUTH_CERTKEY)) + authtype_idx = self.comboAuthType.findData(self._cfg.getSettings(Config.AUTH_TYPE)) + if authtype_idx <= 0: + authtype_idx = 0 + self.lineCACertFile.setEnabled(False) + self.lineCertFile.setEnabled(False) + self.lineCertKeyFile.setEnabled(False) + self.comboAuthType.setCurrentIndex(authtype_idx) + + self.comboUIRules.blockSignals(True) + self.comboUIRules.setCurrentIndex(self._cfg.getInt(self._cfg.DEFAULT_IGNORE_TEMPORARY_RULES)) + self.checkUIRules.setChecked(self._cfg.getBool(self._cfg.DEFAULT_IGNORE_RULES)) + self.comboUIRules.setEnabled(self._cfg.getBool(self._cfg.DEFAULT_IGNORE_RULES)) + + #self._set_rules_duration_filter() + + self._cfg.setRulesDurationFilter( + self._cfg.getBool(self._cfg.DEFAULT_IGNORE_RULES), + self._cfg.getInt(self._cfg.DEFAULT_IGNORE_TEMPORARY_RULES) + ) + self.comboUIRules.blockSignals(False) + + # by default, if no configuration exists, enable notifications. + self.groupNotifs.setChecked(self._cfg.getBool(Config.NOTIFICATIONS_ENABLED, True)) + self.radioSysNotifs.setChecked( + True if self._cfg.getInt(Config.NOTIFICATIONS_TYPE) == Config.NOTIFICATION_TYPE_SYSTEM and self._desktop_notifications.is_available() == True else False + ) + self.radioQtNotifs.setChecked( + True if self._cfg.getInt(Config.NOTIFICATIONS_TYPE) == Config.NOTIFICATION_TYPE_QT or self._desktop_notifications.is_available() == False else False + ) + + ## db + self.dbType = self._cfg.getInt(self._cfg.DEFAULT_DB_TYPE_KEY) + self.comboDBType.setCurrentIndex(self.dbType) + if self.comboDBType.currentIndex() != Database.DB_TYPE_MEMORY: + self.dbFileButton.setVisible(True) + self.dbLabel.setVisible(True) + self.dbLabel.setText(self._cfg.getSettings(self._cfg.DEFAULT_DB_FILE_KEY)) + dbMaxDays = self._cfg.getInt(self._cfg.DEFAULT_DB_MAX_DAYS, 1) + dbJrnlWal = self._cfg.getBool(self._cfg.DEFAULT_DB_JRNL_WAL) + dbPurgeInterval = self._cfg.getInt(self._cfg.DEFAULT_DB_PURGE_INTERVAL, 5) + self._enable_db_cleaner_options(self._cfg.getBool(Config.DEFAULT_DB_PURGE_OLDEST), dbMaxDays) + self._enable_db_jrnl_wal(self._cfg.getBool(Config.DEFAULT_DB_PURGE_OLDEST), dbJrnlWal) + self.spinDBMaxDays.setValue(dbMaxDays) + self.spinDBPurgeInterval.setValue(dbPurgeInterval) + + self._load_themes() + self._load_node_settings() + self._load_ui_settings() + + def _load_ui_settings(self): + self._ui_refresh_interval = self._cfg.getInt(self._cfg.STATS_REFRESH_INTERVAL, 0) + self.spinUIRefresh.setValue(self._ui_refresh_interval) + + saved_lang = self._cfg.getSettings(Config.DEFAULT_LANGUAGE) + if saved_lang: + saved_langname = self._cfg.getSettings(Config.DEFAULT_LANGNAME) + self.comboUILang.blockSignals(True) + self.comboUILang.setCurrentText(saved_langname) + self.comboUILang.blockSignals(False) + + auto_scale = self._cfg.getBool(Config.QT_AUTO_SCREEN_SCALE_FACTOR, default_value=True) + screen_factor = self._cfg.getSettings(Config.QT_SCREEN_SCALE_FACTOR) + if screen_factor is None or screen_factor == "": + screen_factor = "1" + self.lineUIScreenFactor.setText(screen_factor) + self.checkUIAutoScreen.blockSignals(True) + self.checkUIAutoScreen.setChecked(auto_scale) + self.checkUIAutoScreen.blockSignals(False) + self._show_ui_scalefactor_widgets(auto_scale) + + qt_platform = self._cfg.getSettings(Config.QT_PLATFORM_PLUGIN) + if qt_platform is not None and qt_platform != "": + self.comboUIQtPlatform.setCurrentText(qt_platform) + + self.checkAutostart.setChecked(self._autostart.isEnabled()) + + self._load_ui_columns_config() + + def _load_node_settings(self): + addr = self.comboNodes.currentText() + if addr == "": + return + + try: + node_data = self._node_list[addr]['data'] + self.labelNodeVersion.setText(node_data.version) + self.labelNodeName.setText(node_data.name) + self.comboNodeLogLevel.setCurrentIndex(node_data.logLevel) + + node_config = json.loads(node_data.config) + self.comboNodeAction.setCurrentText(node_config['DefaultAction']) + self.comboNodeDuration.setCurrentText(node_config['DefaultDuration']) + self.comboNodeMonitorMethod.setCurrentText(node_config['ProcMonitorMethod']) + self.checkInterceptUnknown.setChecked(node_config['InterceptUnknown']) + self.comboNodeLogLevel.setCurrentIndex(int(node_config['LogLevel'])) + + if node_config.get('LogUTC') == None: + node_config['LogUTC'] = False + self.checkNodeLogUTC.setChecked(node_config['LogUTC']) + if node_config.get('LogMicro') == None: + node_config['LogMicro'] = False + self.checkNodeLogMicro.setChecked(node_config['LogMicro']) + + if node_config.get('Server') != None: + self.comboNodeAddress.setEnabled(True) + self.comboNodeLogFile.setEnabled(True) + + self.comboNodeAddress.setCurrentText(node_config['Server']['Address']) + self.comboNodeLogFile.setCurrentText(node_config['Server']['LogFile']) + + self._load_node_auth_settings(node_config['Server']) + else: + self.comboNodeAddress.setEnabled(False) + self.comboNodeLogFile.setEnabled(False) + except Exception as e: + print(self.LOG_TAG + "exception loading config: ", e) + + def _load_node_config(self, addr): + try: + if self.comboNodeAddress.currentText() == "": + return None, QC.translate("preferences", "Server address can not be empty") + + node_action = Config.ACTION_DENY + if self.comboNodeAction.currentIndex() == 1: + node_action = Config.ACTION_ALLOW + elif self.comboNodeAction.currentIndex() == 2: + node_action = Config.ACTION_REJECT + + node_duration = Config.DURATION_ONCE + if self.comboNodeDuration.currentIndex() == 1: + node_duration = Config.DURATION_UNTIL_RESTART + elif self.comboNodeDuration.currentIndex() == 2: + node_duration = Config.DURATION_ALWAYS + + node_config = json.loads(self._nodes.get_node_config(addr)) + node_config['DefaultAction'] = node_action + node_config['DefaultDuration'] = node_duration + node_config['ProcMonitorMethod'] = self.comboNodeMonitorMethod.currentText() + node_config['LogLevel'] = self.comboNodeLogLevel.currentIndex() + node_config['LogUTC'] = self.checkNodeLogUTC.isChecked() + node_config['LogMicro'] = self.checkNodeLogMicro.isChecked() + node_config['InterceptUnknown'] = self.checkInterceptUnknown.isChecked() + + if node_config.get('Server') != None: + # skip setting Server Address if we're applying the config to all nodes + node_config['Server']['Address'] = self.comboNodeAddress.currentText() + node_config['Server']['LogFile'] = self.comboNodeLogFile.currentText() + + cfg = self._save_node_auth_config(node_config['Server']) + if cfg != None: + node_config['Server'] = cfg + else: + print(addr, " doesn't have Server item") + return json.dumps(node_config, indent=" "), None + except Exception as e: + print(self.LOG_TAG + "exception loading node config on %s: " % addr, e) + + return None, QC.translate("preferences", "Error loading {0} configuration").format(addr) + + def _load_node_auth_settings(self, config): + try: + if config == None: + return + + auth = config.get('Authentication') + authtype_idx = 0 + if auth != None: + if auth.get('Type') != None: + authtype_idx = self.comboNodeAuthType.findData(auth['Type']) + else: + config['Authentication'] = {} + auth = config.get('Authentication') + + self.lineNodeCACertFile.setEnabled(authtype_idx >= 0) + self.lineNodeServerCertFile.setEnabled(authtype_idx >= 0) + self.lineNodeCertFile.setEnabled(authtype_idx >= 0) + self.lineNodeCertKeyFile.setEnabled(authtype_idx >= 0) + + tls = auth.get('TLSOptions') + if tls != None and authtype_idx >= 0: + if tls.get('CACert') != None: + self.lineNodeCACertFile.setText(tls['CACert']) + if tls.get('ServerCert') != None: + self.lineNodeServerCertFile.setText(tls['ServerCert']) + if tls.get('ClientCert') != None: + self.lineNodeCertFile.setText(tls['ClientCert']) + if tls.get('ClientKey') != None: + self.lineNodeCertKeyFile.setText(tls['ClientKey']) + if tls.get('SkipVerify') != None: + self.checkNodeAuthSkipVerify.setChecked(tls['SkipVerify']) + + if tls.get('ClientAuthType') != None: + clienttype_idx = self.comboNodeAuthVerifyType.findData(tls['ClientAuthType']) + if clienttype_idx >= 0: + self.comboNodeAuthVerifyType.setCurrentIndex(clienttype_idx) + + self.comboNodeAuthType.setCurrentIndex(authtype_idx) + # signals are connected after this method is called + self._cb_combo_node_auth_type_changed(authtype_idx) + except Exception as e: + print("[prefs] load node auth options exception:", e) + self._set_status_error(str(e)) + + def _save_node_auth_config(self, config): + try: + auth = config.get('Authentication') + if auth == None: + auth = {} + + auth['Type'] = self.NODE_AUTH[self.comboNodeAuthType.currentIndex()] + tls = auth.get('TLSOptions') + if tls == None: + tls = {} + + tls['CACert'] = self.lineNodeCACertFile.text() + tls['ServerCert'] = self.lineNodeServerCertFile.text() + tls['ClientCert'] = self.lineNodeCertFile.text() + tls['ClientKey'] = self.lineNodeCertKeyFile.text() + tls['SkipVerify'] = self.checkNodeAuthSkipVerify.isChecked() + tls['ClientAuthType'] = self.NODE_AUTH_VERIFY[self.comboNodeAuthVerifyType.currentIndex()] + auth['TLSOptions'] = tls + config['Authentication'] = auth + + return config + except Exception as e: + print("[prefs] node auth options exception:", e) + self._set_status_error(str(e)) + return None + + def _load_ui_columns_config(self): + cols = self._cfg.getSettings(Config.STATS_SHOW_COLUMNS) + if cols == None: + return + + for c in range(13): + checked = str(c) in cols + + if c == 0: + self.checkHideTime.setChecked(checked) + elif c == 1: + self.checkHideNode.setChecked(checked) + elif c == 2: + self.checkHideAction.setChecked(checked) + elif c == 3: + self.checkHideSrcPort.setChecked(checked) + elif c == 4: + self.checkHideSrcIP.setChecked(checked) + elif c == 5: + self.checkHideDstIP.setChecked(checked) + elif c == 6: + self.checkHideDstHost.setChecked(checked) + elif c == 7: + self.checkHideDstPort.setChecked(checked) + elif c == 8: + self.checkHideProto.setChecked(checked) + elif c == 9: + self.checkHideUID.setChecked(checked) + elif c == 10: + self.checkHidePID.setChecked(checked) + elif c == 11: + self.checkHideProc.setChecked(checked) + elif c == 12: + self.checkHideCmdline.setChecked(checked) + elif c == 13: + self.checkHideRule.setChecked(checked) + + def _reset_node_settings(self): + self.comboNodeAction.setCurrentIndex(0) + self.comboNodeDuration.setCurrentIndex(0) + self.comboNodeMonitorMethod.setCurrentIndex(0) + self.checkInterceptUnknown.setChecked(False) + self.comboNodeLogLevel.setCurrentIndex(0) + self.checkNodeLogUTC.setChecked(True) + self.checkNodeLogMicro.setChecked(False) + self.labelNodeName.setText("") + self.labelNodeVersion.setText("") + self.comboNodeAuthType.setCurrentIndex(self.AUTH_SIMPLE) + self.lineNodeCACertFile.setText("") + self.lineNodeServerCertFile.setText("") + self.lineNodeCertFile.setText("") + self.lineNodeCertKeyFile.setText("") + self.checkNodeAuthSkipVerify.setChecked(False) + self.comboNodeAuthVerifyType.setCurrentIndex(0) + self._cb_combo_node_auth_type_changed(0) + + def _save_settings(self): + self._reset_status_message() + self._show_status_label() + self._save_ui_config() + if not self._save_db_config(): + return + self._save_nodes_config() + + self.saved.emit() + self._settingsSaved = True + self._needs_restart() + + def _save_db_config(self): + dbtype = self.comboDBType.currentIndex() + db_name = self._cfg.getSettings(self._cfg.DEFAULT_DB_FILE_KEY) + + if self.dbLabel.text() != "" and \ + (self.comboDBType.currentIndex() != self.dbType or db_name != self.dbLabel.text()): + self._changes_needs_restart = QC.translate("preferences", "DB type changed") + + if self.comboDBType.currentIndex() != Database.DB_TYPE_MEMORY: + if self.dbLabel.text() != "": + db_name = self.dbLabel.text() + else: + Message.ok( + QC.translate("preferences", "Warning"), + QC.translate("preferences", "You must select a file for the database<br>or choose \"In memory\" type."), + QtWidgets.QMessageBox.Warning) + self.dbLabel.setText("") + return False + else: + db_name = Database.DB_IN_MEMORY + + self._cfg.setSettings(Config.DEFAULT_DB_FILE_KEY, db_name) + self._cfg.setSettings(Config.DEFAULT_DB_TYPE_KEY, dbtype) + self._cfg.setSettings(Config.DEFAULT_DB_PURGE_OLDEST, bool(self.checkDBMaxDays.isChecked())) + self._cfg.setSettings(Config.DEFAULT_DB_MAX_DAYS, int(self.spinDBMaxDays.value())) + self._cfg.setSettings(Config.DEFAULT_DB_PURGE_INTERVAL, int(self.spinDBPurgeInterval.value())) + self._cfg.setSettings(Config.DEFAULT_DB_JRNL_WAL, bool(self.checkDBJrnlWal.isChecked())) + self.dbType = self.comboDBType.currentIndex() + + return True + + def _save_ui_config(self): + try: + self._save_ui_columns_config() + + maxmsgsize = self.comboGrpcMsgSize.currentText() + if maxmsgsize != "": + self._cfg.setSettings(Config.DEFAULT_SERVER_MAX_MESSAGE_LENGTH, maxmsgsize.replace(" ", "")) + + savedauthtype = self._cfg.getSettings(Config.AUTH_TYPE) + authtype = self.comboAuthType.itemData(self.comboAuthType.currentIndex()) + cacert = self._cfg.getSettings(Config.AUTH_CA_CERT) + cert = self._cfg.getSettings(Config.AUTH_CERT) + certkey = self._cfg.getSettings(Config.AUTH_CERTKEY) + if not self._validate_certs(): + return + + if savedauthtype != authtype or self.lineCertFile.text() != cert or \ + self.lineCertKeyFile.text() != certkey or self.lineCACertFile.text() != cacert: + self._changes_needs_restart = QC.translate("preferences", "Certificates changed") + self._cfg.setSettings(Config.AUTH_TYPE, authtype) + self._cfg.setSettings(Config.AUTH_CA_CERT, self.lineCACertFile.text()) + self._cfg.setSettings(Config.AUTH_CERT, self.lineCertFile.text()) + self._cfg.setSettings(Config.AUTH_CERTKEY, self.lineCertKeyFile.text()) + + selected_lang = self.comboUILang.itemData(self.comboUILang.currentIndex()) + saved_lang = self._cfg.getSettings(Config.DEFAULT_LANGUAGE) + saved_lang = "" if saved_lang is None else saved_lang + if saved_lang != selected_lang: + languages.save(self._cfg, selected_lang) + self._changes_needs_restart = QC.translate("preferences", "Language changed") + + self._cfg.setSettings(self._cfg.DEFAULT_IGNORE_TEMPORARY_RULES, int(self.comboUIRules.currentIndex())) + self._cfg.setSettings(self._cfg.DEFAULT_IGNORE_RULES, bool(self.checkUIRules.isChecked())) + #self._set_rules_duration_filter() + self._cfg.setRulesDurationFilter( + bool(self.checkUIRules.isChecked()), + int(self.comboUIRules.currentIndex()) + ) + if self.checkUIRules.isChecked(): + self._nodes.delete_rule_by_field(Config.DURATION_FIELD, Config.RULES_DURATION_FILTER) + + self._cfg.setSettings(self._cfg.STATS_REFRESH_INTERVAL, int(self.spinUIRefresh.value())) + self._cfg.setSettings(self._cfg.DEFAULT_ACTION_KEY, self.comboUIAction.currentIndex()) + self._cfg.setSettings(self._cfg.DEFAULT_DURATION_KEY, int(self.comboUIDuration.currentIndex())) + self._cfg.setSettings(self._cfg.DEFAULT_TARGET_KEY, self.comboUITarget.currentIndex()) + self._cfg.setSettings(self._cfg.DEFAULT_TIMEOUT_KEY, self.spinUITimeout.value()) + self._cfg.setSettings(self._cfg.DEFAULT_DISABLE_POPUPS, bool(self.popupsCheck.isChecked())) + self._cfg.setSettings(self._cfg.DEFAULT_POPUP_POSITION, int(self.comboUIDialogPos.currentIndex())) + + self._cfg.setSettings(self._cfg.DEFAULT_POPUP_ADVANCED, bool(self.showAdvancedCheck.isChecked())) + self._cfg.setSettings(self._cfg.DEFAULT_POPUP_ADVANCED_DSTIP, bool(self.dstIPCheck.isChecked())) + self._cfg.setSettings(self._cfg.DEFAULT_POPUP_ADVANCED_DSTPORT, bool(self.dstPortCheck.isChecked())) + self._cfg.setSettings(self._cfg.DEFAULT_POPUP_ADVANCED_UID, bool(self.uidCheck.isChecked())) + + self._cfg.setSettings(self._cfg.NOTIFICATIONS_ENABLED, bool(self.groupNotifs.isChecked())) + self._cfg.setSettings(self._cfg.NOTIFICATIONS_TYPE, + int(Config.NOTIFICATION_TYPE_SYSTEM if self.radioSysNotifs.isChecked() else Config.NOTIFICATION_TYPE_QT)) + + self._themes.save_theme(self.comboUITheme.currentIndex(), self.comboUITheme.currentText(), str(self.spinUIDensity.value())) + + qt_platform = self._cfg.getSettings(Config.QT_PLATFORM_PLUGIN) + if qt_platform != self.comboUIQtPlatform.currentText(): + self._changes_needs_restart = QC.translate("preferences", "Qt platform plugin changed") + self._cfg.setSettings(Config.QT_PLATFORM_PLUGIN, self.comboUIQtPlatform.currentText()) + + self._cfg.setSettings(Config.QT_AUTO_SCREEN_SCALE_FACTOR, bool(self.checkUIAutoScreen.isChecked())) + self._cfg.setSettings(Config.QT_SCREEN_SCALE_FACTOR, self.lineUIScreenFactor.text()) + + if self._themes.available() and self._saved_theme != "" and self.comboUITheme.currentText() == QC.translate("preferences", "System"): + self._changes_needs_restart = QC.translate("preferences", "UI theme changed") + + # this is a workaround for not display pop-ups. + # see #79 for more information. + if self.popupsCheck.isChecked(): + self._cfg.setSettings(self._cfg.DEFAULT_TIMEOUT_KEY, 0) + + + self._autostart.enable(self.checkAutostart.isChecked()) + + except Exception as e: + self._set_status_error(str(e)) + + def _save_ui_columns_config(self): + cols=list() + if self.checkHideTime.isChecked(): + cols.append("0") + if self.checkHideNode.isChecked(): + cols.append("1") + if self.checkHideAction.isChecked(): + cols.append("2") + if self.checkHideSrcPort.isChecked(): + cols.append("3") + if self.checkHideSrcIP.isChecked(): + cols.append("4") + if self.checkHideDstIP.isChecked(): + cols.append("5") + if self.checkHideDstHost.isChecked(): + cols.append("6") + if self.checkHideDstPort.isChecked(): + cols.append("7") + if self.checkHideProto.isChecked(): + cols.append("8") + if self.checkHideUID.isChecked(): + cols.append("9") + if self.checkHidePID.isChecked(): + cols.append("10") + if self.checkHideProc.isChecked(): + cols.append("11") + if self.checkHideCmdline.isChecked(): + cols.append("12") + if self.checkHideRule.isChecked(): + cols.append("13") + + self._cfg.setSettings(Config.STATS_SHOW_COLUMNS, cols) + + def _save_nodes_config(self): + addr = self.comboNodes.currentText() + if (self._node_needs_update or self.checkApplyToNodes.isChecked()) and addr != "": + try: + notif = ui_pb2.Notification( + id=int(str(time.time()).replace(".", "")), + type=ui_pb2.CHANGE_CONFIG, + data="", + rules=[]) + if self.checkApplyToNodes.isChecked(): + for addr in self._nodes.get_nodes(): + error = self._save_node_config(notif, addr) + if error != None: + self._set_status_error(error) + return + else: + error = self._save_node_config(notif, addr) + if error != None: + self._set_status_error(error) + return + except Exception as e: + print(self.LOG_TAG + "exception saving config: ", e) + self._set_status_error(QC.translate("preferences", "Exception saving config: {0}").format(str(e))) + elif addr == "": + self._set_status_message(QC.translate("preferences", "There're no nodes connected")) + + self._node_needs_update = False + + def _save_node_config(self, notifObject, addr): + try: + self._set_status_message(QC.translate("preferences", "Applying configuration on {0} ...").format(addr)) + notifObject.data, error = self._load_node_config(addr) + if error != None: + return error + + savedAddr = self._cfg.getSettings(Config.DEFAULT_SERVER_ADDR) + # exclude this message if there're more than one node connected + if self.comboNodes.count() == 1 and savedAddr != None and savedAddr != self.comboNodeAddress.currentText(): + self._changes_needs_restart = QC.translate("preferences", "Ok") + + self._cfg.setSettings(Config.DEFAULT_SERVER_ADDR, self.comboNodeAddress.currentText()) + + self._nodes.save_node_config(addr, notifObject.data) + nid = self._nodes.send_notification(addr, notifObject, self._notification_callback) + self._notifications_sent[nid] = notifObject + + except Exception as e: + print(self.LOG_TAG + "exception saving node config on %s: " % addr, e) + self._set_status_error(QC.translate("preferences", "Exception saving node config {0}: {1}").format((addr, str(e)))) + return addr + ": " + str(e) + + return None + + def _validate_certs(self): + try: + if self.comboAuthType.currentIndex() == PreferencesDialog.AUTH_SIMPLE: + return True + + if self.comboAuthType.currentIndex() > 0 and (self.lineCertFile.text() == "" or self.lineCertKeyFile.text() == ""): + raise ValueError(QC.translate("preferences", "Certs fields cannot be empty.")) + + if oct(stat.S_IMODE(os.lstat(self.lineCertFile.text()).st_mode)) != "0o600": + self._set_status_message( + QC.translate("preferences", "cert file has excessive permissions, it should have 0600") + ) + if oct(stat.S_IMODE(os.lstat(self.lineCertFile.text()).st_mode)) != "0o600": + self._set_status_message( + QC.translate("preferences", "cert key file has excessive permissions, it should have 0600") + ) + + if self.comboAuthType.currentIndex() == PreferencesDialog.AUTH_TLS_MUTUAL: + if oct(stat.S_IMODE(os.lstat(self.lineCACertFile.text()).st_mode)) != "0o600": + self._set_status_message( + QC.translate("preferences", "CA cert file has excessive permissions, it should have 0600") + ) + + return True + except Exception as e: + self._changes_needs_restart = None + self._set_status_error("certs error: {0}".format(e)) + return False + + def _needs_restart(self): + if self._changes_needs_restart: + Message.ok(self._changes_needs_restart, + self._restart_msg, + QtWidgets.QMessageBox.Warning) + self._changes_needs_restart = None + + + def _show_ui_density_widgets(self, idx): + """show ui density widget only for qt-material themes: + https://github.com/UN-GCPDS/qt-material?tab=readme-ov-file#density-scale + """ + hidden = idx == 0 + self.labelUIDensity.setHidden(hidden) + self.spinUIDensity.setHidden(hidden) + self.cmdUIDensityUp.setHidden(hidden) + self.cmdUIDensityDown.setHidden(hidden) + + def _show_ui_scalefactor_widgets(self, show=False): + self.labelUIScreenFactor.setHidden(show) + self.lineUIScreenFactor.setHidden(show) + + def _hide_status_label(self): + self.statusLabel.hide() + + def _show_status_label(self): + self.statusLabel.show() + + def _set_status_error(self, msg): + self._show_status_label() + self.statusLabel.setStyleSheet('color: red') + self.statusLabel.setText(msg) + + def _set_status_successful(self, msg): + self._show_status_label() + self.statusLabel.setStyleSheet('color: green') + self.statusLabel.setText(msg) + + def _set_status_message(self, msg): + self._show_status_label() + self.statusLabel.setStyleSheet('color: darkorange') + self.statusLabel.setText(msg) + + def _reset_status_message(self): + self.statusLabel.setText("") + self._hide_status_label() + + def _enable_db_cleaner_options(self, enable, db_max_days): + self.checkDBMaxDays.setChecked(enable) + self.spinDBMaxDays.setEnabled(enable) + self.spinDBPurgeInterval.setEnabled(enable) + self.labelDBPurgeInterval.setEnabled(enable) + self.labelDBPurgeDays.setEnabled(enable) + self.labelDBPurgeMinutes.setEnabled(enable) + self.cmdDBMaxDaysUp.setEnabled(enable) + self.cmdDBMaxDaysDown.setEnabled(enable) + self.cmdDBPurgesUp.setEnabled(enable) + self.cmdDBPurgesDown.setEnabled(enable) + + def _enable_db_jrnl_wal(self, enable, db_jrnl_wal): + self.checkDBJrnlWal.setChecked(db_jrnl_wal) + self.checkDBJrnlWal.setEnabled(enable) + + def _change_theme(self): + extra_opts = { + 'density_scale': str(self.spinUIDensity.value()) + } + self._themes.change_theme(self, self.comboUITheme.currentText(), extra_opts) + + @QtCore.pyqtSlot(ui_pb2.NotificationReply) + def _cb_notification_callback(self, reply): + #print(self.LOG_TAG, "Config notification received: ", reply.id, reply.code) + if reply.id in self._notifications_sent: + if reply.code == ui_pb2.OK: + self._set_status_successful(QC.translate("preferences", "Configuration applied.")) + else: + self._set_status_error(QC.translate("preferences", "Error applying configuration: {0}").format(reply.data)) + + del self._notifications_sent[reply.id] + + def _cb_line_certs_changed(self, text): + self._changes_needs_restart = QC.translate("preferences", "Certs changed") + + def _cb_node_line_certs_changed(self, text): + self._changes_needs_restart = QC.translate("preferences", "Node certs changed") + self._node_needs_update = True + + def _cb_file_db_clicked(self): + options = QtWidgets.QFileDialog.Options() + fileName, _ = QtWidgets.QFileDialog.getSaveFileName(self, "", "","All Files (*)", options=options) + if fileName: + self.dbLabel.setText(fileName) + + def _cb_combo_uirules_changed(self, idx): + self._cfg.setRulesDurationFilter( + self._cfg.getBool(self._cfg.DEFAULT_IGNORE_RULES), + idx + #self._cfg.getInt(self._cfg.DEFAULT_IGNORE_TEMPORARY_RULES) + ) + + + def _cb_db_type_changed(self): + isDBMem = self.comboDBType.currentIndex() == Database.DB_TYPE_MEMORY + self.dbFileButton.setVisible(not isDBMem) + self.dbLabel.setVisible(not isDBMem) + self.checkDBMaxDays.setChecked(self._cfg.getBool(Config.DEFAULT_DB_PURGE_OLDEST)) + self.checkDBJrnlWal.setEnabled(not isDBMem) + self.checkDBJrnlWal.setChecked(False) + + def _cb_accept_button_clicked(self): + self.accept() + if not self._settingsSaved: + self._save_settings() + + def _cb_apply_button_clicked(self): + self._reset_status_message() + self._save_settings() + + def _cb_cancel_button_clicked(self): + self.reject() + + def _cb_help_button_clicked(self): + QuickHelp.show( + QC.translate("preferences", + "Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href=\"{0}\">{0}</a>" + ).format(Config.HELP_URL) + ) + + def _cb_popups_check_toggled(self, checked): + self.spinUITimeout.setEnabled(not checked) + if not checked: + self.spinUITimeout.setValue(20) + + def _cb_node_combo_changed(self, index): + self._load_node_settings() + + def _cb_node_needs_update(self): + self._node_needs_update = True + + def _cb_ui_check_rules_toggled(self, state): + self.comboUIRules.setEnabled(state) + + def _cb_combo_themes_changed(self, index): + self._change_theme() + self._show_ui_density_widgets(index) + + def _cb_spin_uidensity_changed(self, value): + self._change_theme() + + def _cb_ui_check_auto_scale_toggled(self, checked): + self._changes_needs_restart = QC.translate("preferences", "Auto scale option changed") + self._show_ui_scalefactor_widgets(checked) + + def _cb_ui_screen_factor_changed(self, text): + self._changes_needs_restart = QC.translate("preferences", "Screen factor option changed") + + def _cb_combo_auth_type_changed(self, index): + curtype = self.comboAuthType.itemData(self.comboAuthType.currentIndex()) + savedtype = self._cfg.getSettings(Config.AUTH_TYPE) + if curtype != savedtype: + self._changes_needs_restart = QC.translate("preferences", "Auth type changed") + + self.lineCACertFile.setEnabled(index == PreferencesDialog.AUTH_TLS_MUTUAL) + self.lineCertFile.setEnabled(index >= PreferencesDialog.AUTH_TLS_SIMPLE) + self.lineCertKeyFile.setEnabled(index >= PreferencesDialog.AUTH_TLS_SIMPLE) + + def _cb_combo_node_auth_type_changed(self, index): + curtype = self.comboNodeAuthType.itemData(self.comboNodeAuthType.currentIndex()) + #savedtype = self._cfg.getSettings(Config.AUTH_TYPE) + #if curtype != savedtype: + # self._changes_needs_restart = QC.translate("preferences", "Auth type changed") + + self.lineNodeCACertFile.setEnabled(index == PreferencesDialog.AUTH_TLS_MUTUAL) + self.lineNodeServerCertFile.setEnabled(index >= PreferencesDialog.AUTH_TLS_SIMPLE) + self.lineNodeCertFile.setEnabled(index >= PreferencesDialog.AUTH_TLS_SIMPLE) + self.lineNodeCertKeyFile.setEnabled(index >= PreferencesDialog.AUTH_TLS_SIMPLE) + self.checkNodeAuthSkipVerify.setEnabled(index >= PreferencesDialog.AUTH_TLS_SIMPLE) + self.comboNodeAuthVerifyType.setEnabled(index >= PreferencesDialog.AUTH_TLS_SIMPLE) + + self._node_needs_update = True + + def _cb_db_max_days_toggled(self, state): + self._enable_db_cleaner_options(state, 1) + + def _cb_db_jrnl_wal_toggled(self, state): + self._changes_needs_restart = QC.translate("preferences", "DB journal_mode changed") + + def _cb_cmd_spin_clicked(self, spinWidget, operation): + if operation == self.SUM: + spinWidget.setValue(spinWidget.value() + 1) + else: + spinWidget.setValue(spinWidget.value() - 1) + + if spinWidget == self.popupsCheck: + enablePopups = spinWidget.value() > 0 + self.popupsCheck.setChecked(not enablePopups) + self.spinUITimeout.setEnabled(enablePopups) + + def _cb_radio_system_notifications(self): + if self._desktop_notifications.is_available() == False: + self.radioSysNotifs.setChecked(False) + self.radioQtNotifs.setChecked(True) + self._set_status_error(QC.translate("notifications", "System notifications are not available, you need to install python3-notify2.")) + return + + def _cb_test_notifs_clicked(self): + try: + self.cmdTestNotifs.setEnabled(False) + if self._desktop_notifications.is_available() == False: + self._set_status_error(QC.translate("notifications", "System notifications are not available, you need to install python3-notify2.")) + return + + if self.radioSysNotifs.isChecked(): + self._desktop_notifications.show("title", "body") + else: + pass + except Exception as e: + print(self.LOG_TAG + "exception testing notifications:", e) + finally: + self.cmdTestNotifs.setEnabled(True) diff --git a/ui/opensnitch/dialogs/processdetails.py b/ui/opensnitch/dialogs/processdetails.py new file mode 100644 index 0000000..7c4aa20 --- /dev/null +++ b/ui/opensnitch/dialogs/processdetails.py @@ -0,0 +1,334 @@ +import os +import sys +import json + +from PyQt5 import QtCore, QtGui, uic, QtWidgets + +import opensnitch.proto as proto +ui_pb2, ui_pb2_grpc = proto.import_() + +from opensnitch.nodes import Nodes +from opensnitch.desktop_parser import LinuxDesktopParser +from opensnitch.utils import Message, Icons + +DIALOG_UI_PATH = "%s/../res/process_details.ui" % os.path.dirname(sys.modules[__name__].__file__) +class ProcessDetailsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]): + + LOG_TAG = "[ProcessDetails]: " + + _notification_callback = QtCore.pyqtSignal(ui_pb2.NotificationReply) + + TAB_STATUS = 0 + TAB_DESCRIPTORS = 1 + TAB_IOSTATS = 2 + TAB_MAPS = 3 + TAB_STACK = 4 + TAB_ENVS = 5 + + TABS = { + TAB_STATUS: { + "text": None, + "scrollPos": 0 + }, + TAB_DESCRIPTORS: { + "text": None, + "scrollPos": 0 + }, + TAB_IOSTATS: { + "text": None, + "scrollPos": 0 + }, + TAB_MAPS: { + "text": None, + "scrollPos": 0 + }, + TAB_STACK: { + "text": None, + "scrollPos": 0 + }, + TAB_ENVS: { + "text": None, + "scrollPos": 0 + } + } + + def __init__(self, parent=None, appicon=None): + super(ProcessDetailsDialog, self).__init__(parent) + QtWidgets.QDialog.__init__(self, parent, QtCore.Qt.WindowStaysOnTopHint) + self.setWindowFlags(QtCore.Qt.Window) + self.setupUi(self) + self.setWindowIcon(appicon) + + self._app_name = None + self._app_icon = None + self._apps_parser = LinuxDesktopParser() + self._nodes = Nodes.instance() + self._notification_callback.connect(self._cb_notification_callback) + + self._nid = None + self._pid = "" + self._notifications_sent = {} + + self.cmdClose.clicked.connect(self._cb_close_clicked) + self.cmdAction.clicked.connect(self._cb_action_clicked) + self.comboPids.currentIndexChanged.connect(self._cb_combo_pids_changed) + + self.TABS[self.TAB_STATUS]['text'] = self.textStatus + self.TABS[self.TAB_DESCRIPTORS]['text'] = self.textOpenedFiles + self.TABS[self.TAB_IOSTATS]['text'] = self.textIOStats + self.TABS[self.TAB_MAPS]['text'] = self.textMappedFiles + self.TABS[self.TAB_STACK]['text'] = self.textStack + self.TABS[self.TAB_ENVS]['text'] = self.textEnv + + self.TABS[self.TAB_DESCRIPTORS]['text'].setFont(QtGui.QFont("monospace")) + + self.iconStart = QtGui.QIcon.fromTheme("media-playback-start") + self.iconPause = QtGui.QIcon.fromTheme("media-playback-pause") + + if QtGui.QIcon.hasThemeIcon("window-close"): + return + + closeIcon = Icons.new(self, "window-close") + self.cmdClose.setIcon(closeIcon) + self.iconStart = Icons.new(self, "media-playback-start") + self.iconPause = Icons.new(self, "media-playback-pause") + + @QtCore.pyqtSlot(ui_pb2.NotificationReply) + def _cb_notification_callback(self, reply): + if reply.id in self._notifications_sent: + noti = self._notifications_sent[reply.id] + + if reply.code == ui_pb2.ERROR: + self._show_message(QtCore.QCoreApplication.translate("proc_details", "<b>Error loading process information:</b> <br><br>\n\n") + reply.data) + self._pid = "" + self._set_button_running(False) + + # if we haven't loaded any data yet, just close the window + if self._data_loaded == False: + # but if there're more than 1 pid keep the window open. + # we may have one pid already closed and one alive. + if self.comboPids.count() <= 1: + self._close() + + self._delete_notification(reply.id) + return + + if noti.type == ui_pb2.MONITOR_PROCESS and reply.data != "": + self._load_data(reply.data) + + elif noti.type == ui_pb2.STOP_MONITOR_PROCESS: + if reply.data != "": + self._show_message(QtCore.QCoreApplication.translate("proc_details", "<b>Error stopping monitoring process:</b><br><br>") + reply.data) + self._set_button_running(False) + + self._delete_notification(reply.id) + else: + print("[stats] unknown notification received: ", reply.id) + + def closeEvent(self, e): + self._close() + + def _cb_close_clicked(self): + self._close() + + def _cb_combo_pids_changed(self, idx): + if idx == -1: + return + # TODO: this event causes to send to 2 Start notifications + #if self._pid != "" and self._pid != self.comboPids.currentText(): + # self._stop_monitoring() + # self._pid = self.comboPids.currentText() + # self._start_monitoring() + + def _cb_action_clicked(self): + if not self.cmdAction.isChecked(): + self._stop_monitoring() + else: + self._start_monitoring() + + def _show_message(self, text): + Message.ok(text, "", QtWidgets.QMessageBox.Warning) + + def _delete_notification(self, nid): + if nid in self._notifications_sent: + del self._notifications_sent[nid] + + def _reset(self): + self._app_name = None + self._app_icon = None + self.comboPids.clear() + self.labelProcName.setText(QtCore.QCoreApplication.translate("proc_details", "loading...")) + self.labelProcArgs.setText(QtCore.QCoreApplication.translate("proc_details", "loading...")) + self.labelProcIcon.clear() + self.labelStatm.setText("") + self.labelCwd.setText("") + for tidx in range(0, len(self.TABS)): + self.TABS[tidx]['text'].setPlainText("") + + def _set_button_running(self, yes): + if yes: + self.cmdAction.setChecked(True) + self.cmdAction.setIcon(self.iconPause) + else: + self.cmdAction.setChecked(False) + self.cmdAction.setIcon(self.iconStart) + + def _close(self): + self._stop_monitoring() + self.comboPids.clear() + self._pid = "" + self.hide() + + def monitor(self, pids): + if self._pid != "": + self._stop_monitoring() + + self._data_loaded = False + self._pids = pids + self._reset() + for pid in pids: + if pid != None: + self.comboPids.addItem(pid) + + self.show() + self._start_monitoring() + + def _set_tab_text(self, tab_idx, text): + self.TABS[tab_idx]['scrollPos'] = self.TABS[tab_idx]['text'].verticalScrollBar().value() + self.TABS[tab_idx]['text'].setPlainText(text) + self.TABS[tab_idx]['text'].verticalScrollBar().setValue(self.TABS[tab_idx]['scrollPos']) + + def _start_monitoring(self): + try: + # avoid to send notifications without a pid + if self._pid != "": + return + + self._pid = self.comboPids.currentText() + if self._pid == "": + return + + self._set_button_running(True) + noti = ui_pb2.Notification(clientName="", serverName="", type=ui_pb2.MONITOR_PROCESS, data=self._pid, rules=[]) + self._nid = self._nodes.send_notification(self._pids[self._pid], noti, self._notification_callback) + self._notifications_sent[self._nid] = noti + except Exception as e: + print(self.LOG_TAG + "exception starting monitoring: ", e) + + def _stop_monitoring(self): + if self._pid == "": + return + + self._set_button_running(False) + noti = ui_pb2.Notification(clientName="", serverName="", type=ui_pb2.STOP_MONITOR_PROCESS, data=str(self._pid), rules=[]) + self._nid = self._nodes.send_notification(self._pids[self._pid], noti, self._notification_callback) + self._notifications_sent[self._nid] = noti + self._pid = "" + self._app_icon = None + + def _load_data(self, data): + tab_idx = self.tabWidget.currentIndex() + + try: + proc = json.loads(data) + self._load_app_icon(proc['Path']) + if self._app_name != None: + self.labelProcName.setText("<b>" + self._app_name + "</b>") + self.labelProcName.setToolTip("<b>" + self._app_name + "</b>") + + #if proc['Path'] not in proc['Args']: + # proc['Args'].insert(0, proc['Path']) + + self.labelProcArgs.setFixedHeight(30) + self.labelProcArgs.setText(" ".join(proc['Args'])) + self.labelProcArgs.setToolTip(" ".join(proc['Args'])) + self.labelCwd.setText("<b>CWD: </b>" + proc['CWD']) + self.labelCwd.setToolTip("<b>CWD: </b>" + proc['CWD']) + self._load_mem_data(proc['Statm']) + + if tab_idx == self.TAB_STATUS: + self._set_tab_text(tab_idx, proc['Status']) + + elif tab_idx == self.TAB_DESCRIPTORS: + self._load_descriptors(proc['Descriptors']) + + elif tab_idx == self.TAB_IOSTATS: + self._load_iostats(proc['IOStats']) + + elif tab_idx == self.TAB_MAPS: + self._set_tab_text(tab_idx, proc['Maps']) + + elif tab_idx == self.TAB_STACK: + self._set_tab_text(tab_idx, proc['Stack']) + + elif tab_idx == self.TAB_ENVS: + self._load_env_vars(proc['Env']) + + self._data_loaded = True + + except Exception as e: + print(self.LOG_TAG + "exception loading data: ", e) + + def _load_app_icon(self, proc_path): + if self._app_icon != None: + return + + self._app_name, self._app_icon, _, _ = self._apps_parser.get_info_by_path(proc_path, "terminal") + + icon = QtGui.QIcon().fromTheme(self._app_icon) + pixmap = icon.pixmap(icon.actualSize(QtCore.QSize(48, 48))) + self.labelProcIcon.setPixmap(pixmap) + + if self._app_name == None: + self._app_name = proc_path + + def _load_iostats(self, iostats): + ioText = "%-16s %dMB<br>%-16s %dMB<br>%-16s %d<br>%-16s %d<br>%-16s %dMB<br>%-16s %dMB<br>" % ( + "<b>Chars read:</b>", + ((iostats['RChar'] / 1024) / 1024), + "<b>Chars written:</b>", + ((iostats['WChar'] / 1024) / 1024), + "<b>Syscalls read:</b>", + (iostats['SyscallRead']), + "<b>Syscalls write:</b>", + (iostats['SyscallWrite']), + "<b>KB read:</b>", + ((iostats['ReadBytes'] / 1024) / 1024), + "<b>KB written: </b>", + ((iostats['WriteBytes'] / 1024) / 1024) + ) + + self.textIOStats.setPlainText("") + self.textIOStats.appendHtml(ioText) + + def _load_mem_data(self, mem): + # assuming page size == 4096 + pagesize = 4096 + memText = "<b>VIRT:</b> %dMB, <b>RSS:</b> %dMB, <b>Libs:</b> %dMB, <b>Data:</b> %dMB, <b>Text:</b> %dMB" % ( + ((mem['Size'] * pagesize) / 1024) / 1024, + ((mem['Resident'] * pagesize) / 1024) / 1024, + ((mem['Lib'] * pagesize) / 1024) / 1024, + ((mem['Data'] * pagesize) / 1024) / 1024, + ((mem['Text'] * pagesize) / 1024) / 1024 + ) + self.labelStatm.setText(memText) + + def _load_descriptors(self, descriptors): + text = "%-12s%-40s%-8s -> %s\n\n" % ("Size", "Time", "Name", "Symlink") + for d in descriptors: + text += "{:<12}{:<40}{:<8} -> {}\n".format(str(d['Size']), d['ModTime'], d['Name'], d['SymLink']) + + self._set_tab_text(self.TAB_DESCRIPTORS, text) + + def _load_env_vars(self, envs): + if envs == {}: + self._set_tab_text(self.TAB_ENVS, "<no environment variables>") + return + + text = "%-15s\t%s\n\n" % ("Name", "Value") + for env_name in envs: + text += "%-15s:\t%s\n" % (env_name, envs[env_name]) + + self._set_tab_text(self.TAB_ENVS, text) + + diff --git a/ui/opensnitch/dialogs/prompt.py b/ui/opensnitch/dialogs/prompt.py new file mode 100644 index 0000000..2adf46c --- /dev/null +++ b/ui/opensnitch/dialogs/prompt.py @@ -0,0 +1,775 @@ +import threading +import sys +import time +import os +import os.path +import pwd +import json +import ipaddress +from datetime import datetime + +from PyQt5 import QtCore, QtGui, uic, QtWidgets +from PyQt5.QtCore import QCoreApplication as QC, QEvent + +from slugify import slugify + +from opensnitch.utils import Icons +from opensnitch.desktop_parser import LinuxDesktopParser +from opensnitch.config import Config +from opensnitch.version import version +from opensnitch.actions import Actions +from opensnitch.rules import Rules, Rule + +import opensnitch.proto as proto +ui_pb2, ui_pb2_grpc = proto.import_() + +DIALOG_UI_PATH = "%s/../res/prompt.ui" % os.path.dirname(sys.modules[__name__].__file__) +class PromptDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]): + _prompt_trigger = QtCore.pyqtSignal() + _tick_trigger = QtCore.pyqtSignal() + _timeout_trigger = QtCore.pyqtSignal() + + DEFAULT_TIMEOUT = 15 + + # don't translate + FIELD_REGEX_HOST = "regex_host" + FIELD_REGEX_IP = "regex_ip" + FIELD_PROC_PATH = "process_path" + FIELD_PROC_ARGS = "process_args" + FIELD_PROC_ID = "process_id" + FIELD_USER_ID = "user_id" + FIELD_DST_IP = "dst_ip" + FIELD_DST_PORT = "dst_port" + FIELD_DST_NETWORK = "dst_network" + FIELD_DST_HOST = "simple_host" + FIELD_APPIMAGE = "appimage_path" + + DURATION_30s = "30s" + DURATION_5m = "5m" + DURATION_15m = "15m" + DURATION_30m = "30m" + DURATION_1h = "1h" + # don't translate + + APPIMAGE_PREFIX = "/tmp/.mount_" + + # label displayed in the pop-up combo + DURATION_session = QC.translate("popups", "until reboot") + # label displayed in the pop-up combo + DURATION_forever = QC.translate("popups", "forever") + + def __init__(self, parent=None, appicon=None): + QtWidgets.QDialog.__init__(self, parent, QtCore.Qt.WindowStaysOnTopHint) + # Other interesting flags: QtCore.Qt.Tool | QtCore.Qt.BypassWindowManagerHint + self._cfg = Config.get() + self._rules = Rules.instance() + + self.setupUi(self) + self.setWindowIcon(appicon) + self.installEventFilter(self) + + self._width = self.width() + self._height = self.height() + self.reset_widgets() + + dialog_geometry = self._cfg.getSettings("promptDialog/geometry") + if dialog_geometry == QtCore.QByteArray: + self.restoreGeometry(dialog_geometry) + + self.setWindowTitle("OpenSnitch v%s" % version) + + self._lock = threading.Lock() + self._con = None + self._rule = None + self._local = True + self._peer = None + self._prompt_trigger.connect(self.on_connection_prompt_triggered) + self._timeout_trigger.connect(self.on_timeout_triggered) + self._tick_trigger.connect(self.on_tick_triggered) + self._tick = int(self._cfg.getSettings(self._cfg.DEFAULT_TIMEOUT_KEY)) if self._cfg.hasKey(self._cfg.DEFAULT_TIMEOUT_KEY) else self.DEFAULT_TIMEOUT + self._tick_thread = None + self._done = threading.Event() + self._timeout_text = "" + self._timeout_triggered = False + + self._apps_parser = LinuxDesktopParser() + + self.whatIPCombo.setVisible(False) + self.checkDstIP.setVisible(False) + self.checkDstPort.setVisible(False) + self.checkUserID.setVisible(False) + self.appDescriptionLabel.setVisible(False) + + self._ischeckAdvanceded = False + self.checkAdvanced.toggled.connect(self._check_advanced_toggled) + + self.checkAdvanced.clicked.connect(self._button_clicked) + self.durationCombo.activated.connect(self._button_clicked) + self.whatCombo.activated.connect(self._button_clicked) + self.whatIPCombo.activated.connect(self._button_clicked) + self.checkDstIP.clicked.connect(self._button_clicked) + self.checkDstPort.clicked.connect(self._button_clicked) + self.checkUserID.clicked.connect(self._button_clicked) + + self.allowIcon = Icons.new(self, "emblem-default") + denyIcon = Icons.new(self, "emblem-important") + rejectIcon = Icons.new(self, "window-close") + + self._default_action = self._cfg.getInt(self._cfg.DEFAULT_ACTION_KEY) + + self.allowButton.clicked.connect(lambda: self._on_action_clicked(Config.ACTION_ALLOW_IDX)) + self.allowButton.setIcon(self.allowIcon) + self._allow_text = QC.translate("popups", "Allow") + self._action_text = [ + QC.translate("popups", "Deny"), + QC.translate("popups", "Allow"), + QC.translate("popups", "Reject") + ] + self._action_icon = [denyIcon, self.allowIcon, rejectIcon] + + m = QtWidgets.QMenu() + m.addAction(denyIcon, self._action_text[Config.ACTION_DENY_IDX]).triggered.connect( + lambda: self._on_action_clicked(Config.ACTION_DENY_IDX) + ) + m.addAction(self.allowIcon, self._action_text[Config.ACTION_ALLOW_IDX]).triggered.connect( + lambda: self._on_action_clicked(Config.ACTION_ALLOW_IDX) + ) + m.addAction(rejectIcon, self._action_text[Config.ACTION_REJECT_IDX]).triggered.connect( + lambda: self._on_action_clicked(Config.ACTION_REJECT_IDX) + ) + self.actionButton.setMenu(m) + self.actionButton.setText(self._action_text[Config.ACTION_DENY_IDX]) + self.actionButton.setIcon(self._action_icon[Config.ACTION_DENY_IDX]) + if self._default_action != Config.ACTION_ALLOW_IDX: + self.actionButton.setText(self._action_text[self._default_action]) + self.actionButton.setIcon(self._action_icon[self._default_action]) + self.actionButton.clicked.connect(self._on_deny_btn_clicked) + + def eventFilter(self, obj, event): + if event.type() == QEvent.MouseButtonPress: + self._stop_countdown() + return True + return False + + def showEvent(self, event): + super(PromptDialog, self).showEvent(event) + self.activateWindow() + self.adjust_size() + self.move_popup() + + def reset_widgets(self): + # Don't allow labels to grow more than the dialog's width. + # This can happen if the path or the binary name is too large. + + self.appNameLabel.setMaximumWidth(self._width-5) + self.appDescriptionLabel.setMaximumWidth(self._width-5) + self.appPathLabel.setMaximumWidth(self._width-5) + self.argsLabel.setMaximumWidth(self._width-5) + self.messageLabel.setMaximumWidth(self._width-5) + + self.appNameLabel.setText("") + self.appDescriptionLabel.setText("") + self.appPathLabel.setText("") + self.argsLabel.setText("") + self.messageLabel.setText("") + + def adjust_size(self): + if self._width is None or self._height is None: + self._width = self.width() + self._height = self.height() + + self.resize(QtCore.QSize(self._width, self._height)) + + def move_popup(self): + popup_pos = self._cfg.getInt(self._cfg.DEFAULT_POPUP_POSITION) + point = QtWidgets.QDesktopWidget().availableGeometry() + if popup_pos == self._cfg.POPUP_TOP_RIGHT: + self.move(point.topRight()) + elif popup_pos == self._cfg.POPUP_TOP_LEFT: + self.move(point.topLeft()) + elif popup_pos == self._cfg.POPUP_BOTTOM_RIGHT: + self.move(point.bottomRight()) + elif popup_pos == self._cfg.POPUP_BOTTOM_LEFT: + self.move(point.bottomLeft()) + + def _stop_countdown(self): + action_idx = self._cfg.getInt(self._cfg.DEFAULT_ACTION_KEY) + if action_idx == Config.ACTION_ALLOW_IDX: + self.allowButton.setText(self._allow_text) + self.allowButton.setIcon(self.allowIcon) + else: + self.actionButton.setText(self._action_text[action_idx]) + self.actionButton.setIcon(self._action_icon[action_idx]) + self._tick_thread.stop = True + + def _check_advanced_toggled(self, state): + self.checkDstIP.setVisible(state) + self.whatIPCombo.setVisible(state) + self.destIPLabel.setVisible(not state) + self.checkDstPort.setVisible(state == True and (self._con != None and self._con.dst_port != 0)) + self.checkUserID.setVisible(state) + + self._ischeckAdvanceded = state + self.adjust_size() + self.move_popup() + + def _button_clicked(self): + self._stop_countdown() + + def truncate_text(self, text, max_size=64): + if len(text) > max_size: + text = text[:max_size] + "..." + return text + + def _set_elide_text(self, widget, text, max_size=64): + text = self.truncate_text(text, max_size) + widget.setText(text) + + def promptUser(self, connection, is_local, peer): + # one at a time + with self._lock: + # reset state + self.reset_widgets() + if self._tick_thread != None and self._tick_thread.is_alive(): + self._tick_thread.join() + + self._cfg.reload() + self._tick = int(self._cfg.getSettings(self._cfg.DEFAULT_TIMEOUT_KEY)) if self._cfg.hasKey(self._cfg.DEFAULT_TIMEOUT_KEY) else self.DEFAULT_TIMEOUT + self._tick_thread = threading.Thread(target=self._timeout_worker) + self._tick_thread.stop = self._ischeckAdvanceded + self._timeout_triggered = False + self._rule = None + self._local = is_local + self._peer = peer + self._con = connection + self._done.clear() + # trigger and show dialog + self._prompt_trigger.emit() + # start timeout thread + self._tick_thread.start() + # wait for user choice or timeout + self._done.wait() + + return self._rule, self._timeout_triggered + + def _timeout_worker(self): + if self._tick == 0: + self._timeout_trigger.emit() + return + + while self._tick > 0 and self._done.is_set() is False: + t = threading.currentThread() + # stop only stops the coundtdown, not the thread itself. + if getattr(t, "stop", True): + self._tick = int(self._cfg.getSettings(self._cfg.DEFAULT_TIMEOUT_KEY)) + time.sleep(1) + continue + + self._tick -= 1 + self._tick_trigger.emit() + time.sleep(1) + + if not self._done.is_set(): + self._timeout_trigger.emit() + + @QtCore.pyqtSlot() + def on_connection_prompt_triggered(self): + self._render_connection(self._con) + if self._tick > 0: + self.show() + + @QtCore.pyqtSlot() + def on_tick_triggered(self): + self._set_cmd_action_text() + + @QtCore.pyqtSlot() + def on_timeout_triggered(self): + self._timeout_triggered = True + self._send_rule() + + def _hide_widget(self, widget, hide): + widget.setVisible(not hide) + + def _configure_default_duration(self): + if self._cfg.hasKey(self._cfg.DEFAULT_DURATION_KEY): + cur_idx = self._cfg.getInt(self._cfg.DEFAULT_DURATION_KEY) + self.durationCombo.setCurrentIndex(cur_idx) + else: + self.durationCombo.setCurrentIndex(self._cfg.DEFAULT_DURATION_IDX) + + def _set_cmd_action_text(self): + action_idx = self._cfg.getInt(self._cfg.DEFAULT_ACTION_KEY) + if action_idx == Config.ACTION_ALLOW_IDX: + self.allowButton.setText("{0} ({1})".format(self._allow_text, self._tick)) + self.allowButton.setIcon(self.allowIcon) + self.actionButton.setText(self._action_text[Config.ACTION_DENY_IDX]) + else: + self.allowButton.setText(self._allow_text) + self.actionButton.setText("{0} ({1})".format(self._action_text[action_idx], self._tick)) + self.actionButton.setIcon(self._action_icon[action_idx]) + + def _set_app_description(self, description): + if description != None and description != "": + self.appDescriptionLabel.setVisible(True) + self.appDescriptionLabel.setFixedHeight(50) + self.appDescriptionLabel.setToolTip(description) + self._set_elide_text(self.appDescriptionLabel, "%s" % description) + else: + self.appDescriptionLabel.setVisible(False) + self.appDescriptionLabel.setFixedHeight(0) + self.appDescriptionLabel.setText("") + return + + self.appDescriptionLabel.setText( + "".join( + filter(str.isprintable, self.appDescriptionLabel.text()) + ) + ) + + def _set_app_path(self, app_name, app_args, con): + # show the binary path if it's not part of the cmdline args: + # cmdline: telnet 1.1.1.1 (path: /usr/bin/telnet.netkit) + # cmdline: /usr/bin/telnet.netkit 1.1.1.1 (the binary path is part of the cmdline args, no need to display it) + if con.process_path != "" and len(con.process_args) >= 1 and con.process_path not in con.process_args: + self.appPathLabel.setToolTip("Process path: %s" % con.process_path) + if app_name.lower() == app_args: + self._set_elide_text(self.appPathLabel, "%s" % con.process_path) + else: + self._set_elide_text(self.appPathLabel, "(%s)" % con.process_path) + self.appPathLabel.setVisible(True) + elif con.process_path != "" and len(con.process_args) == 0: + self._set_elide_text(self.appPathLabel, "%s" % con.process_path) + self.appPathLabel.setVisible(True) + else: + self.appPathLabel.setVisible(False) + self.appPathLabel.setText("") + return + + self.appPathLabel.setText( + "".join( + filter(str.isprintable, self.appPathLabel.text()) + ) + ) + if self.appPathLabel.width() >= self._width: + self.appPathLabel.setText("\u200b".join(self.appPathLabel.text())) + + def _set_app_args(self, app_name, app_args): + # if the app name and the args are the same, there's no need to display + # the args label (amule for example) + if app_name.lower() != app_args: + self.argsLabel.setVisible(True) + self._set_elide_text(self.argsLabel, app_args, 256) + self.argsLabel.setToolTip(app_args) + else: + self.argsLabel.setVisible(False) + self.argsLabel.setText("") + return + + self.argsLabel.setText( + "".join( + filter(str.isprintable, self.argsLabel.text()) + ) + ) + if self.argsLabel.width() >= self._width: + self.argsLabel.setText("\u200b".join(self.argsLabel.text())) + + def _set_default_target(self, combo, con, app_name, app_args): + # set appimage as default target if the process path starts with + # /tmp/._mount + if con.process_path.startswith(self.APPIMAGE_PREFIX): + idx = combo.findData(self.FIELD_APPIMAGE) + if idx != -1: + combo.setCurrentIndex(idx) + return + + if int(con.process_id) > 0 and app_name != "" and app_args != "": + self.whatCombo.setCurrentIndex(int(self._cfg.getSettings(self._cfg.DEFAULT_TARGET_KEY))) + else: + self.whatCombo.setCurrentIndex(2) + + def _render_connection(self, con): + app_name, app_icon, description, _ = self._apps_parser.get_info_by_path(con.process_path, "terminal") + app_args = " ".join(con.process_args) + self._set_app_description(description) + self._set_app_path(app_name, app_args, con) + self._set_app_args(app_name, app_args) + + if app_name == "": + self.appPathLabel.setVisible(False) + self.argsLabel.setVisible(False) + app_name = QC.translate("popups", "Unknown process %s" % con.process_path) + self.appNameLabel.setText(QC.translate("popups", "Outgoing connection")) + else: + self._set_elide_text(self.appNameLabel, "%s" % app_name, max_size=42) + self.appNameLabel.setToolTip(app_name) + + self.cwdLabel.setToolTip("%s %s" % (QC.translate("popups", "Process launched from:"), con.process_cwd)) + self._set_elide_text(self.cwdLabel, con.process_cwd, max_size=32) + + pixmap = self._get_app_icon(app_icon) + self.iconLabel.setPixmap(pixmap) + + message = self._get_popup_message(app_name, con) + + self.messageLabel.setText(message) + self.messageLabel.setToolTip(message) + + self.sourceIPLabel.setText(con.src_ip) + self.destIPLabel.setText(con.dst_ip) + if con.dst_port == 0: + self.destPortLabel.setText("") + else: + self.destPortLabel.setText(str(con.dst_port)) + self._hide_widget(self.destPortLabel, con.dst_port == 0) + self._hide_widget(self.destPortLabel_1, con.dst_port == 0) + self._hide_widget(self.checkDstPort, con.dst_port == 0 or not self._ischeckAdvanceded) + + if self._local: + try: + uid = "%d (%s)" % (con.user_id, pwd.getpwuid(con.user_id).pw_name) + except: + uid = "" + else: + uid = "%d" % con.user_id + + self.uidLabel.setText(uid) + self.pidLabel.setText("%s" % con.process_id) + + self.whatCombo.clear() + self.whatIPCombo.clear() + + # the order of these combobox entries must match those in the preferences dialog + # prefs -> UI -> Default target + self.whatCombo.addItem(QC.translate("popups", "from this executable"), self.FIELD_PROC_PATH) + if int(con.process_id) < 0: + self.whatCombo.model().item(0).setEnabled(False) + + self.whatCombo.addItem(QC.translate("popups", "from this command line"), self.FIELD_PROC_ARGS) + + self.whatCombo.addItem(QC.translate("popups", "to port {0}").format(con.dst_port), self.FIELD_DST_PORT) + self.whatCombo.addItem(QC.translate("popups", "to {0}").format(con.dst_ip), self.FIELD_DST_IP) + + self.whatCombo.addItem(QC.translate("popups", "from user {0}").format(uid), self.FIELD_USER_ID) + if int(con.user_id) < 0: + self.whatCombo.model().item(4).setEnabled(False) + + self.whatCombo.addItem(QC.translate("popups", "from this PID"), self.FIELD_PROC_ID) + ####################### + + if con.process_path.startswith(self.APPIMAGE_PREFIX): + self._add_appimage_pattern_to_combo(self.whatCombo, con) + + self._add_dst_networks_to_combo(self.whatCombo, con.dst_ip) + + if con.dst_host != "" and con.dst_host != con.dst_ip: + self._add_dsthost_to_combo(con.dst_host) + + self.whatIPCombo.addItem(QC.translate("popups", "to {0}").format(con.dst_ip), self.FIELD_DST_IP) + + parts = con.dst_ip.split('.') + nparts = len(parts) + for i in range(1, nparts): + self.whatCombo.addItem(QC.translate("popups", "to {0}.*").format('.'.join(parts[:i])), self.FIELD_REGEX_IP) + self.whatIPCombo.addItem(QC.translate("popups", "to {0}.*").format( '.'.join(parts[:i])), self.FIELD_REGEX_IP) + + self._add_dst_networks_to_combo(self.whatIPCombo, con.dst_ip) + + self._default_action = self._cfg.getInt(self._cfg.DEFAULT_ACTION_KEY) + + self._configure_default_duration() + + self._set_default_target(self.whatCombo, con, app_name, app_args) + + self.checkDstIP.setChecked(self._cfg.getBool(self._cfg.DEFAULT_POPUP_ADVANCED_DSTIP)) + self.checkDstPort.setChecked(self._cfg.getBool(self._cfg.DEFAULT_POPUP_ADVANCED_DSTPORT)) + self.checkUserID.setChecked(self._cfg.getBool(self._cfg.DEFAULT_POPUP_ADVANCED_UID)) + if self._cfg.getBool(self._cfg.DEFAULT_POPUP_ADVANCED): + self.checkAdvanced.toggle() + + self._set_cmd_action_text() + self.checkAdvanced.setFocus() + + self.setFixedSize(self.size()) + + # https://gis.stackexchange.com/questions/86398/how-to-disable-the-escape-key-for-a-dialog + def keyPressEvent(self, event): + if not event.key() == QtCore.Qt.Key_Escape: + super(PromptDialog, self).keyPressEvent(event) + + # prevent a click on the window's x + # from quitting the whole application + def closeEvent(self, e): + self._send_rule() + e.ignore() + + def _add_appimage_pattern_to_combo(self, combo, con): + """appimages' absolute path usually starts with /tmp/.mount_ + """ + appimage_bin = os.path.basename(con.process_path) + appimage_path = os.path.dirname(con.process_path) + appimage_path = appimage_path[0:len(self.APPIMAGE_PREFIX)+6] + combo.addItem( + QC.translate("popups", "from {0}*/{1}").format(appimage_path, appimage_bin), + self.FIELD_APPIMAGE + ) + + def _add_dst_networks_to_combo(self, combo, dst_ip): + if type(ipaddress.ip_address(dst_ip)) == ipaddress.IPv4Address: + combo.addItem(QC.translate("popups", "to {0}").format(ipaddress.ip_network(dst_ip + "/24", strict=False)), self.FIELD_DST_NETWORK) + combo.addItem(QC.translate("popups", "to {0}").format(ipaddress.ip_network(dst_ip + "/16", strict=False)), self.FIELD_DST_NETWORK) + combo.addItem(QC.translate("popups", "to {0}").format(ipaddress.ip_network(dst_ip + "/8", strict=False)), self.FIELD_DST_NETWORK) + else: + combo.addItem(QC.translate("popups", "to {0}").format(ipaddress.ip_network(dst_ip + "/64", strict=False)), self.FIELD_DST_NETWORK) + combo.addItem(QC.translate("popups", "to {0}").format(ipaddress.ip_network(dst_ip + "/128", strict=False)), self.FIELD_DST_NETWORK) + + def _add_dsthost_to_combo(self, dst_host): + self.whatCombo.addItem("%s" % dst_host, self.FIELD_DST_HOST) + self.whatIPCombo.addItem("%s" % dst_host, self.FIELD_DST_HOST) + + parts = dst_host.split('.')[1:] + nparts = len(parts) + for i in range(0, nparts - 1): + self.whatCombo.addItem(QC.translate("popups", "to *.{0}").format('.'.join(parts[i:])), self.FIELD_REGEX_HOST) + self.whatIPCombo.addItem(QC.translate("popups", "to *.{0}").format('.'.join(parts[i:])), self.FIELD_REGEX_HOST) + + + def _get_app_icon(self, app_icon): + """we try to get the icon of an app from the system. + If it's not found, then we'll try to search for it in common directories + of the system. + """ + try: + icon = QtGui.QIcon().fromTheme(app_icon) + pixmap = icon.pixmap(icon.actualSize(QtCore.QSize(48, 48))) + if QtGui.QIcon().hasThemeIcon(app_icon) == False or pixmap.height() == 0: + # sometimes the icon is an absolute path, sometimes it's not + if os.path.isabs(app_icon): + icon = QtGui.QIcon(app_icon) + pixmap = icon.pixmap(icon.actualSize(QtCore.QSize(48, 48))) + else: + icon_path = self._apps_parser.discover_app_icon(app_icon) + if icon_path != None: + icon = QtGui.QIcon(icon_path) + pixmap = icon.pixmap(icon.actualSize(QtCore.QSize(48, 48))) + except Exception as e: + print("Exception _get_app_icon():", e) + + return pixmap + + def _get_popup_message(self, app_name, con): + """ + _get_popup_message helps constructing the message that is displayed on + the pop-up dialog. Example: + curl is connecting to www.opensnitch.io on TCP port 443 + """ + app_name = self.truncate_text(app_name) + message = "<b>%s</b>" % app_name + if not self._local: + message = QC.translate("popups", "<b>Remote</b> process %s running on <b>%s</b>") % ( \ + message, + self._peer.split(':')[1]) + + msg_action = QC.translate("popups", "is connecting to <b>%s</b> on %s port %d") % ( \ + con.dst_host or con.dst_ip, + con.protocol.upper(), + con.dst_port ) + + # icmp port is 0 (i.e.: no port) + if con.dst_port == 0: + msg_action = QC.translate("popups", "is connecting to <b>%s</b>, %s") % ( \ + con.dst_host or con.dst_ip, + con.protocol.upper() ) + + if con.dst_port == 53 and con.dst_ip != con.dst_host and con.dst_host != "": + msg_action = QC.translate("popups", "is attempting to resolve <b>%s</b> via %s, %s port %d") % ( \ + con.dst_host, + con.dst_ip, + con.protocol.upper(), + con.dst_port) + + if self.messageLabel.width() >= self._width: + self.messageLabel.setText("\u200b".join(self.messageLabel.text())) + + return "%s %s" % (message, msg_action) + + def _get_duration(self, duration_idx): + if duration_idx == 0: + return Config.DURATION_ONCE + elif duration_idx == 1: + return self.DURATION_30s + elif duration_idx == 2: + return self.DURATION_5m + elif duration_idx == 3: + return self.DURATION_15m + elif duration_idx == 4: + return self.DURATION_30m + elif duration_idx == 5: + return self.DURATION_1h + elif duration_idx == 6: + return Config.DURATION_UNTIL_RESTART + else: + return Config.DURATION_ALWAYS + + def _get_combo_operator(self, combo, what_idx, con): + if combo.itemData(what_idx) == self.FIELD_PROC_PATH: + return Config.RULE_TYPE_SIMPLE, Config.OPERAND_PROCESS_PATH, con.process_path + + elif combo.itemData(what_idx) == self.FIELD_PROC_ARGS: + # this should not happen + if len(con.process_args) == 0 or con.process_args[0] == "": + return Config.RULE_TYPE_SIMPLE, Config.OPERAND_PROCESS_PATH, con.process_path + return Config.RULE_TYPE_SIMPLE, Config.OPERAND_PROCESS_COMMAND, ' '.join(con.process_args) + + elif combo.itemData(what_idx) == self.FIELD_PROC_ID: + return Config.RULE_TYPE_SIMPLE, Config.OPERAND_PROCESS_ID, "{0}".format(con.process_id) + + elif combo.itemData(what_idx) == self.FIELD_USER_ID: + return Config.RULE_TYPE_SIMPLE, Config.OPERAND_USER_ID, "%s" % con.user_id + + elif combo.itemData(what_idx) == self.FIELD_DST_PORT: + return Config.RULE_TYPE_SIMPLE, Config.OPERAND_DEST_PORT, "%s" % con.dst_port + + elif combo.itemData(what_idx) == self.FIELD_DST_IP: + return Config.RULE_TYPE_SIMPLE, Config.OPERAND_DEST_IP, con.dst_ip + + elif combo.itemData(what_idx) == self.FIELD_DST_HOST: + return Config.RULE_TYPE_SIMPLE, Config.OPERAND_DEST_HOST, combo.currentText() + + elif combo.itemData(what_idx) == self.FIELD_DST_NETWORK: + # strip "to ": "to x.x.x/20" -> "x.x.x/20" + # we assume that to is one word in all languages + parts = combo.currentText().split(' ') + text = parts[len(parts)-1] + return Config.RULE_TYPE_NETWORK, Config.OPERAND_DEST_NETWORK, text + + elif combo.itemData(what_idx) == self.FIELD_REGEX_HOST: + parts = combo.currentText().split(' ') + text = parts[len(parts)-1] + # ^(|.*\.)yahoo\.com + dsthost = r'\.'.join(text.split('.')).replace("*", "") + dsthost = r'^(|.*\.)%s$' % dsthost[2:] + return Config.RULE_TYPE_REGEXP, Config.OPERAND_DEST_HOST, dsthost + + elif combo.itemData(what_idx) == self.FIELD_REGEX_IP: + parts = combo.currentText().split(' ') + text = parts[len(parts)-1] + return Config.RULE_TYPE_REGEXP, Config.OPERAND_DEST_IP, "%s" % r'\.'.join(text.split('.')).replace("*", ".*") + + elif combo.itemData(what_idx) == self.FIELD_APPIMAGE: + appimage_bin = os.path.basename(con.process_path) + appimage_path = os.path.dirname(con.process_path).replace('.', r'\.') + appimage_path = appimage_path[0:len(self.APPIMAGE_PREFIX)+7] + return Config.RULE_TYPE_REGEXP, Config.OPERAND_PROCESS_PATH, r'^{0}[0-9A-Za-z]{{6}}\/.*{1}$'.format(appimage_path, appimage_bin) + + def _on_action_clicked(self, action): + self._default_action = action + self._send_rule() + + def _on_deny_btn_clicked(self, action): + self._default_action = self._cfg.getInt(self._cfg.DEFAULT_ACTION_KEY) + if self._default_action == Config.ACTION_ALLOW_IDX: + self._default_action = Config.ACTION_DENY_IDX + self._send_rule() + + def _is_list_rule(self): + return self.checkUserID.isChecked() or self.checkDstPort.isChecked() or self.checkDstIP.isChecked() + + def _get_rule_name(self, rule): + rule_temp_name = slugify("%s %s" % (rule.action, rule.duration)) + if self._is_list_rule(): + rule_temp_name = "%s-list" % rule_temp_name + else: + rule_temp_name = "%s-simple" % rule_temp_name + rule_temp_name = slugify("%s %s" % (rule_temp_name, rule.operator.data)) + + return rule_temp_name[:128] + + def _send_rule(self): + try: + self._cfg.setSettings("promptDialog/geometry", self.saveGeometry()) + self._rule = ui_pb2.Rule(name="user.choice") + self._rule.created = int(datetime.now().timestamp()) + self._rule.enabled = True + self._rule.duration = self._get_duration(self.durationCombo.currentIndex()) + + self._rule.action = Config.ACTION_ALLOW + if self._default_action == Config.ACTION_DENY_IDX: + self._rule.action = Config.ACTION_DENY + elif self._default_action == Config.ACTION_REJECT_IDX: + self._rule.action = Config.ACTION_REJECT + + what_idx = self.whatCombo.currentIndex() + self._rule.operator.type, self._rule.operator.operand, self._rule.operator.data = self._get_combo_operator(self.whatCombo, what_idx, self._con) + if self._rule.operator.data == "": + print("Invalid rule, discarding: ", self._rule) + self._rule = None + return + + rule_temp_name = self._get_rule_name(self._rule) + self._rule.name = rule_temp_name + + # TODO: move to a method + data=[] + if self.checkDstIP.isChecked() and self.whatCombo.itemData(what_idx) != self.FIELD_DST_IP: + _type, _operand, _data = self._get_combo_operator(self.whatIPCombo, self.whatIPCombo.currentIndex(), self._con) + data.append({"type": _type, "operand": _operand, "data": _data}) + rule_temp_name = slugify("%s %s" % (rule_temp_name, _data)) + + if self.checkDstPort.isChecked() and self.whatCombo.itemData(what_idx) != self.FIELD_DST_PORT: + data.append({"type": Config.RULE_TYPE_SIMPLE, "operand": Config.OPERAND_DEST_PORT, "data": str(self._con.dst_port)}) + rule_temp_name = slugify("%s %s" % (rule_temp_name, str(self._con.dst_port))) + + if self.checkUserID.isChecked() and self.whatCombo.itemData(what_idx) != self.FIELD_USER_ID: + data.append({"type": Config.RULE_TYPE_SIMPLE, "operand": Config.OPERAND_USER_ID, "data": str(self._con.user_id)}) + rule_temp_name = slugify("%s %s" % (rule_temp_name, str(self._con.user_id))) + + is_list_rule = self._is_list_rule() + + # If the user has selected to filter by cmdline, but the launched + # command path is not absolute or the first component contains + # "/proc/" (/proc/self/fd.., /proc/1234/fd...), we can't trust it. + # In these cases, also filter by the absolute path to the binary. + if self._rule.operator.operand == Config.OPERAND_PROCESS_COMMAND: + proc_args = " ".join(self._con.process_args) + proc_args = proc_args.split(" ") + if os.path.isabs(proc_args[0]) == False or proc_args[0].startswith("/proc"): + is_list_rule = True + data.append({"type": Config.RULE_TYPE_SIMPLE, "operand": Config.OPERAND_PROCESS_PATH, "data": str(self._con.process_path)}) + + if is_list_rule: + data.append({ + "type": self._rule.operator.type, + "operand": self._rule.operator.operand, + "data": self._rule.operator.data + }) + # We need to send back the operator list to the AskRule() call + # as json string, in order to add it to the DB. + self._rule.operator.data = json.dumps(data) + self._rule.operator.type = Config.RULE_TYPE_LIST + self._rule.operator.operand = Config.RULE_TYPE_LIST + for op in data: + self._rule.operator.list.extend([ + ui_pb2.Operator( + type=op['type'], + operand=op['operand'], + sensitive=False if op.get('sensitive') == None else op['sensitive'], + data="" if op.get('data') == None else op['data'] + ) + ]) + + exists = self._rules.exists(self._rule, self._peer) + if not exists: + self._rule.name = self._rules.new_unique_name(rule_temp_name, self._peer, "") + + self.hide() + if self._ischeckAdvanceded: + self.checkAdvanced.toggle() + self._ischeckAdvanceded = False + + except Exception as e: + print("[pop-up] exception creating a rule:", e) + finally: + # signal that the user took a decision and + # a new rule is available + self._done.set() + self.hide() diff --git a/ui/opensnitch/dialogs/ruleseditor.py b/ui/opensnitch/dialogs/ruleseditor.py new file mode 100644 index 0000000..84d99fd --- /dev/null +++ b/ui/opensnitch/dialogs/ruleseditor.py @@ -0,0 +1,1066 @@ + +from PyQt5 import QtCore, QtGui, uic, QtWidgets +from PyQt5.QtCore import QCoreApplication as QC +from slugify import slugify +from datetime import datetime +import re +import sys +import os +import pwd +import time +import ipaddress + +import opensnitch.proto as proto +ui_pb2, ui_pb2_grpc = proto.import_() + +from opensnitch.config import Config +from opensnitch.nodes import Nodes +from opensnitch.database import Database +from opensnitch.database.enums import RuleFields, ConnFields +from opensnitch.version import version +from opensnitch.utils import ( + Message, + FileDialog, + Icons, + NetworkInterfaces, + qvalidator +) +from opensnitch.rules import Rule, Rules + +DIALOG_UI_PATH = "%s/../res/ruleseditor.ui" % os.path.dirname(sys.modules[__name__].__file__) +class RulesEditorDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]): + + LOG_TAG = "[rules editor]" + classA_net = r'10\.\d{1,3}\.\d{1,3}\.\d{1,3}' + classB_net = r'172\.1[6-9]\.\d+\.\d+|172\.2[0-9]\.\d+\.\d+|172\.3[0-1]+\.\d{1,3}\.\d{1,3}' + classC_net = r'192\.168\.\d{1,3}\.\d{1,3}' + others_net = r'127\.\d{1,3}\.\d{1,3}\.\d{1,3}|169\.254\.\d{1,3}\.\d{1,3}' + multinets = r'2[32][23459]\.\d{1,3}\.\d{1,3}\.\d{1,3}' + MULTICAST_RANGE = "^(" + multinets + ")$" + LAN_RANGES = "^(" + others_net + "|" + classC_net + "|" + classB_net + "|" + classA_net + "|::1|f[cde].*::.*)$" + LAN_LABEL = "LAN" + MULTICAST_LABEL = "MULTICAST" + + INVALID_RULE_NAME_CHARS = '/' + + ADD_RULE = 0 + EDIT_RULE = 1 + WORK_MODE = ADD_RULE + + PW_USER = 0 + PW_UID = 2 + + _notification_callback = QtCore.pyqtSignal(ui_pb2.NotificationReply) + + def __init__(self, parent=None, _rule=None, appicon=None): + super(RulesEditorDialog, self).__init__(parent) + + self._notifications_sent = {} + self._nodes = Nodes.instance() + self._db = Database.instance() + self._rules = Rules.instance() + self._notification_callback.connect(self._cb_notification_callback) + self._old_rule_name = None + + self.setupUi(self) + self.setWindowIcon(appicon) + + self.ruleNameValidator = qvalidator.RestrictChars(RulesEditorDialog.INVALID_RULE_NAME_CHARS) + self.ruleNameValidator.result.connect(self._cb_rule_name_validator_result) + self.ruleNameEdit.setValidator(self.ruleNameValidator) + + self.buttonBox.setStandardButtons( + QtWidgets.QDialogButtonBox.Help | + QtWidgets.QDialogButtonBox.Reset | + QtWidgets.QDialogButtonBox.Close | + QtWidgets.QDialogButtonBox.Save + ) + + self.buttonBox.button(QtWidgets.QDialogButtonBox.Reset).clicked.connect(self._cb_reset_clicked) + self.buttonBox.button(QtWidgets.QDialogButtonBox.Close).clicked.connect(self._cb_close_clicked) + self.buttonBox.button(QtWidgets.QDialogButtonBox.Save).clicked.connect(self._cb_save_clicked) + self.buttonBox.button(QtWidgets.QDialogButtonBox.Help).clicked.connect(self._cb_help_clicked) + self.selectListButton.clicked.connect(self._cb_select_list_button_clicked) + self.selectListRegexpButton.clicked.connect(self._cb_select_regexp_list_button_clicked) + self.selectIPsListButton.clicked.connect(self._cb_select_ips_list_button_clicked) + self.selectNetsListButton.clicked.connect(self._cb_select_nets_list_button_clicked) + self.protoCheck.toggled.connect(self._cb_proto_check_toggled) + self.procCheck.toggled.connect(self._cb_proc_check_toggled) + self.cmdlineCheck.toggled.connect(self._cb_cmdline_check_toggled) + self.ifaceCheck.toggled.connect(self._cb_iface_check_toggled) + self.dstPortCheck.toggled.connect(self._cb_dstport_check_toggled) + self.srcPortCheck.toggled.connect(self._cb_srcport_check_toggled) + self.uidCheck.toggled.connect(self._cb_uid_check_toggled) + self.pidCheck.toggled.connect(self._cb_pid_check_toggled) + self.srcIPCheck.toggled.connect(self._cb_srcip_check_toggled) + self.dstIPCheck.toggled.connect(self._cb_dstip_check_toggled) + self.dstHostCheck.toggled.connect(self._cb_dsthost_check_toggled) + self.dstListsCheck.toggled.connect(self._cb_dstlists_check_toggled) + self.dstListRegexpCheck.toggled.connect(self._cb_dstregexplists_check_toggled) + self.dstListIPsCheck.toggled.connect(self._cb_dstiplists_check_toggled) + self.dstListNetsCheck.toggled.connect(self._cb_dstnetlists_check_toggled) + self.uidCombo.currentIndexChanged.connect(self._cb_uid_combo_changed) + + self._users_list = pwd.getpwall() + + if QtGui.QIcon.hasThemeIcon("emblem-default"): + return + + applyIcon = Icons.new(self, "emblem-default") + denyIcon = Icons.new(self, "emblem-important") + rejectIcon = Icons.new(self, "window-close") + openIcon = Icons.new(self, "document-open") + self.actionAllowRadio.setIcon(applyIcon) + self.actionDenyRadio.setIcon(denyIcon) + self.actionRejectRadio.setIcon(rejectIcon) + self.selectListButton.setIcon(openIcon) + self.selectListRegexpButton.setIcon(openIcon) + self.selectNetsListButton.setIcon(openIcon) + self.selectIPsListButton.setIcon(openIcon) + + if _rule != None: + self._load_rule(rule=_rule) + + def showEvent(self, event): + super(RulesEditorDialog, self).showEvent(event) + + # save old combo values so we don't overwrite them here. + oldIface = self.ifaceCombo.currentText() + oldUid = self.uidCombo.currentText() + self.ifaceCombo.clear() + self.uidCombo.clear() + if self._nodes.is_local(self.nodesCombo.currentText()): + self.ifaceCombo.addItems(NetworkInterfaces.list().keys()) + try: + for ip in NetworkInterfaces.list().values(): + if self.srcIPCombo.findText(ip) == -1: + self.srcIPCombo.insertItem(0, ip) + if self.dstIPCombo.findText(ip) == -1: + self.dstIPCombo.insertItem(0, ip) + + self._users_list = pwd.getpwall() + self.uidCombo.blockSignals(True); + for user in self._users_list: + self.uidCombo.addItem("{0} ({1})".format(user[self.PW_USER], user[self.PW_UID]), user[self.PW_UID]) + except Exception as e: + print("[ruleseditor] Error adding IPs:", e) + finally: + self.uidCombo.blockSignals(False); + self.ifaceCombo.setCurrentText(oldIface) + self.uidCombo.setCurrentText(oldUid) + + def _bool(self, s): + return s == 'True' + + def _cb_rule_name_validator_result(self, result): + if result == QtGui.QValidator.Invalid: + self._set_status_error( + QC.translate("rules", + "Invalid rule name (not allowed characters: '{0}' )".format(RulesEditorDialog.INVALID_RULE_NAME_CHARS) + ) + ) + else: + self._set_status_message("") + + def _cb_accept_clicked(self): + pass + + def _cb_close_clicked(self): + self.hide() + + def _cb_reset_clicked(self): + self._reset_state() + + def _cb_help_clicked(self): + QtGui.QDesktopServices.openUrl(QtCore.QUrl(Config.HELP_URL)) + + def _cb_select_list_button_clicked(self): + dirName = FileDialog.select_dir(self, self.dstListsLine.text()) + if dirName != None and dirName != "": + self.dstListsLine.setText(dirName) + + def _cb_select_nets_list_button_clicked(self): + dirName = FileDialog.select_dir(self, self.dstListNetsLine.text()) + if dirName != None and dirName != "": + self.dstListNetsLine.setText(dirName) + + def _cb_select_ips_list_button_clicked(self): + dirName = FileDialog.select_dir(self, self.dstListIPsLine.text()) + if dirName != None and dirName != "": + self.dstListIPsLine.setText(dirName) + + def _cb_select_regexp_list_button_clicked(self): + dirName = FileDialog.select_dir(self, self.dstRegexpListsLine.text()) + if dirName != None and dirName != "": + self.dstRegexpListsLine.setText(dirName) + + def _cb_proto_check_toggled(self, state): + self.protoCombo.setEnabled(state) + + def _cb_proc_check_toggled(self, state): + self.procLine.setEnabled(state) + self.checkProcRegexp.setEnabled(state) + + def _cb_cmdline_check_toggled(self, state): + self.cmdlineLine.setEnabled(state) + self.checkCmdlineRegexp.setEnabled(state) + + def _cb_iface_check_toggled(self, state): + self.ifaceCombo.setEnabled(state) + + def _cb_dstport_check_toggled(self, state): + self.dstPortLine.setEnabled(state) + + def _cb_srcport_check_toggled(self, state): + self.srcPortLine.setEnabled(state) + + def _cb_uid_check_toggled(self, state): + self.uidCombo.setEnabled(state) + + def _cb_pid_check_toggled(self, state): + self.pidLine.setEnabled(state) + + def _cb_srcip_check_toggled(self, state): + self.srcIPCombo.setEnabled(state) + + def _cb_dstip_check_toggled(self, state): + self.dstIPCombo.setEnabled(state) + + def _cb_dsthost_check_toggled(self, state): + self.dstHostLine.setEnabled(state) + + def _cb_dstlists_check_toggled(self, state): + self.dstListsLine.setEnabled(state) + self.selectListButton.setEnabled(state) + + def _cb_dstregexplists_check_toggled(self, state): + self.dstRegexpListsLine.setEnabled(state) + self.selectListRegexpButton.setEnabled(state) + + def _cb_dstiplists_check_toggled(self, state): + self.dstListIPsLine.setEnabled(state) + self.selectIPsListButton.setEnabled(state) + + def _cb_dstnetlists_check_toggled(self, state): + self.dstListNetsLine.setEnabled(state) + self.selectNetsListButton.setEnabled(state) + + def _cb_uid_combo_changed(self, index): + self.uidCombo.setCurrentText(str(self._users_list[index][self.PW_UID])) + + def _set_status_error(self, msg): + self.statusLabel.setStyleSheet('color: red') + self.statusLabel.setText(msg) + + def _set_status_message(self, msg): + self.statusLabel.setStyleSheet('color: green') + self.statusLabel.setText(msg) + + def _cb_save_clicked(self): + if self.nodesCombo.count() == 0: + self._set_status_error(QC.translate("rules", "There're no nodes connected.")) + return + + rule_name = self.ruleNameEdit.text() + if rule_name == "": + return + + node = self.nodesCombo.currentText() + # avoid to overwrite rules when: + # - adding a new rule. + # - when a rule is renamed, i.e., the rule is edited or added and the + # user changes the name. + if self.WORK_MODE == self.ADD_RULE and self._db.get_rule(rule_name, node).next() == True: + self._set_status_error(QC.translate("rules", "There's already a rule with this name.")) + return + elif self.WORK_MODE == self.EDIT_RULE and rule_name != self._old_rule_name and \ + self._db.get_rule(rule_name, node).next() == True: + self._set_status_error(QC.translate("rules", "There's already a rule with this name.")) + return + + result, error = self._save_rule() + if result == False: + self._set_status_error(error) + return + + self._add_rule() + if self._old_rule_name != None and self._old_rule_name != self.rule.name: + self._delete_rule() + + self._old_rule_name = rule_name + + # after adding a new rule, we enter into EDIT mode, to allow further + # changes without closing the dialog. + if self.WORK_MODE == self.ADD_RULE: + self.WORK_MODE = self.EDIT_RULE + + self._rules.updated.emit(0) + + @QtCore.pyqtSlot(ui_pb2.NotificationReply) + def _cb_notification_callback(self, reply): + #print(self.LOG_TAG, "Rule notification received: ", reply.id, reply.code) + if reply.id in self._notifications_sent: + if reply.code == ui_pb2.OK: + self._set_status_message(QC.translate("rules", "Rule applied.")) + else: + self._set_status_error(QC.translate("rules", "Error applying rule: {0}").format(reply.data)) + + del self._notifications_sent[reply.id] + + def _get_duration(self, duration_idx): + if duration_idx == 0: + return Config.DURATION_ONCE + elif duration_idx == 1: + return Config.DURATION_30s + elif duration_idx == 2: + return Config.DURATION_5m + elif duration_idx == 3: + return Config.DURATION_15m + elif duration_idx == 4: + return Config.DURATION_30m + elif duration_idx == 5: + return Config.DURATION_1h + elif duration_idx == 6: + return Config.DURATION_UNTIL_RESTART + else: + return Config.DURATION_ALWAYS + + def _load_duration(self, duration): + if duration == Config.DURATION_ONCE: + return 0 + elif duration == Config.DURATION_30s: + return 1 + elif duration == Config.DURATION_5m: + return 2 + elif duration == Config.DURATION_15m: + return 3 + elif duration == Config.DURATION_30m: + return 4 + elif duration == Config.DURATION_1h: + return 5 + elif duration == Config.DURATION_UNTIL_RESTART: + return 6 + else: + # always + return 7 + + def _is_regex(self, text): + charset="\\*{[|^?$" + for c in charset: + if c in text: + return True + return False + + def _is_valid_regex(self, regex): + try: + re.compile(regex) + return True + except re.error as e: + self.statusLabel.setText(str(e)) + return False + + def _is_valid_list_path(self, listWidget): + if listWidget.text() == "": + return QC.translate("rules", "Lists field cannot be empty") + if self._nodes.is_local(self.nodesCombo.currentText()) and \ + self.nodeApplyAllCheck.isChecked() == False and \ + os.path.isdir(listWidget.text()) == False: + return QC.translate("rules", "Lists field must be a directory") + + return None + + def set_fields_from_connection(self, records): + self.nodesCombo.setCurrentText(records.value(ConnFields.Node)) + self.protoCombo.setCurrentText(records.value(ConnFields.Protocol).upper()) + self.srcIPCombo.setCurrentText(records.value(ConnFields.SrcIP)) + self.dstIPCombo.setCurrentText(records.value(ConnFields.DstIP)) + self.dstHostLine.setText(records.value(ConnFields.DstHost)) + self.dstPortLine.setText(records.value(ConnFields.DstPort)) + self.srcPortLine.setText(records.value(ConnFields.SrcPort)) + self.uidCombo.setCurrentText(records.value(ConnFields.UID)) + self.pidLine.setText(records.value(ConnFields.PID)) + self.procLine.setText(records.value(ConnFields.Process)) + self.cmdlineLine.setText(records.value(ConnFields.Cmdline)) + + def _reset_state(self): + self._old_rule_name = None + self.rule = None + + self.ruleNameEdit.setText("") + self.ruleDescEdit.setPlainText("") + self.statusLabel.setText("") + + self.actionDenyRadio.setChecked(True) + self.durationCombo.setCurrentIndex(0) + + self.protoCheck.setChecked(False) + self.protoCombo.setCurrentText("") + + self.procCheck.setChecked(False) + self.checkProcRegexp.setEnabled(False) + self.checkProcRegexp.setChecked(False) + self.procLine.setText("") + + self.cmdlineCheck.setChecked(False) + self.checkCmdlineRegexp.setEnabled(False) + self.checkCmdlineRegexp.setChecked(False) + self.cmdlineLine.setText("") + + self.uidCheck.setChecked(False) + self.uidCombo.setCurrentText("") + + self.pidCheck.setChecked(False) + self.pidLine.setText("") + + self.ifaceCheck.setChecked(False) + self.ifaceCombo.setCurrentText("") + + self.dstPortCheck.setChecked(False) + self.dstPortLine.setText("") + + self.srcPortCheck.setChecked(False) + self.srcPortLine.setText("") + + self.srcIPCheck.setChecked(False) + self.srcIPCombo.setCurrentText("") + + self.dstIPCheck.setChecked(False) + self.dstIPCombo.setCurrentText("") + + self.dstHostCheck.setChecked(False) + self.dstHostLine.setText("") + + self.selectListButton.setEnabled(False) + self.dstListsCheck.setChecked(False) + self.dstListsLine.setText("") + + self.selectListRegexpButton.setEnabled(False) + self.dstListRegexpCheck.setChecked(False) + self.dstRegexpListsLine.setText("") + + self.selectIPsListButton.setEnabled(False) + self.dstListIPsCheck.setChecked(False) + self.dstListIPsLine.setText("") + + self.selectNetsListButton.setEnabled(False) + self.dstListNetsCheck.setChecked(False) + self.dstListNetsLine.setText("") + + def _load_rule(self, addr=None, rule=None): + if self._load_nodes(addr) == False: + return False + + self.ruleNameEdit.setText(rule.name) + self.ruleDescEdit.setPlainText(rule.description) + self.enableCheck.setChecked(rule.enabled) + self.precedenceCheck.setChecked(rule.precedence) + self.nologCheck.setChecked(rule.nolog) + if rule.action == Config.ACTION_DENY: + self.actionDenyRadio.setChecked(True) + elif rule.action == Config.ACTION_ALLOW: + self.actionAllowRadio.setChecked(True) + elif rule.action == Config.ACTION_REJECT: + self.actionRejectRadio.setChecked(True) + + self.durationCombo.setCurrentIndex(self._load_duration(self.rule.duration)) + + if self.rule.operator.type != Config.RULE_TYPE_LIST: + self._load_rule_operator(self.rule.operator) + else: + for op in self.rule.operator.list: + self._load_rule_operator(op) + + return True + + def _load_rule_operator(self, operator): + self.sensitiveCheck.setChecked(operator.sensitive) + if operator.operand == Config.OPERAND_PROTOCOL: + self.protoCheck.setChecked(True) + self.protoCombo.setEnabled(True) + self.protoCombo.setCurrentText(operator.data.upper()) + + if operator.operand == Config.OPERAND_PROCESS_PATH: + self.procCheck.setChecked(True) + self.procLine.setEnabled(True) + self.procLine.setText(operator.data) + self.checkProcRegexp.setEnabled(True) + self.checkProcRegexp.setChecked(operator.type == Config.RULE_TYPE_REGEXP) + + if operator.operand == Config.OPERAND_PROCESS_COMMAND: + self.cmdlineCheck.setChecked(True) + self.cmdlineLine.setEnabled(True) + self.cmdlineLine.setText(operator.data) + self.checkCmdlineRegexp.setEnabled(True) + self.checkCmdlineRegexp.setChecked(operator.type == Config.RULE_TYPE_REGEXP) + + if operator.operand == Config.OPERAND_USER_ID: + self.uidCheck.setChecked(True) + self.uidCombo.setEnabled(True) + self.uidCombo.setCurrentText(operator.data) + + if operator.operand == Config.OPERAND_PROCESS_ID: + self.pidCheck.setChecked(True) + self.pidLine.setEnabled(True) + self.pidLine.setText(operator.data) + + if operator.operand == Config.OPERAND_IFACE_OUT: + self.ifaceCheck.setChecked(True) + self.ifaceCombo.setEnabled(True) + self.ifaceCombo.setCurrentText(operator.data) + + if operator.operand == Config.OPERAND_SOURCE_PORT: + self.srcPortCheck.setChecked(True) + self.srcPortLine.setEnabled(True) + self.srcPortLine.setText(operator.data) + + if operator.operand == Config.OPERAND_DEST_PORT: + self.dstPortCheck.setChecked(True) + self.dstPortLine.setEnabled(True) + self.dstPortLine.setText(operator.data) + + if operator.operand == Config.OPERAND_SOURCE_IP or operator.operand == Config.OPERAND_SOURCE_NETWORK: + self.srcIPCheck.setChecked(True) + self.srcIPCombo.setEnabled(True) + if operator.data == self.LAN_RANGES: + self.srcIPCombo.setCurrentText(self.LAN_LABEL) + elif operator.data == self.MULTICAST_RANGE: + self.srcIPCombo.setCurrentText(self.MULTICAST_LABEL) + else: + self.srcIPCombo.setCurrentText(operator.data) + + if operator.operand == Config.OPERAND_DEST_IP or operator.operand == Config.OPERAND_DEST_NETWORK: + self.dstIPCheck.setChecked(True) + self.dstIPCombo.setEnabled(True) + if operator.data == self.LAN_RANGES: + self.dstIPCombo.setCurrentText(self.LAN_LABEL) + elif operator.data == self.MULTICAST_RANGE: + self.dstIPCombo.setCurrentText(self.MULTICAST_LABEL) + else: + self.dstIPCombo.setCurrentText(operator.data) + + if operator.operand == Config.OPERAND_DEST_HOST: + self.dstHostCheck.setChecked(True) + self.dstHostLine.setEnabled(True) + self.dstHostLine.setText(operator.data) + + if operator.operand == Config.OPERAND_LIST_DOMAINS: + self.dstListsCheck.setChecked(True) + self.dstListsCheck.setEnabled(True) + self.dstListsLine.setText(operator.data) + self.selectListButton.setEnabled(True) + + if operator.operand == Config.OPERAND_LIST_DOMAINS_REGEXP: + self.dstListRegexpCheck.setChecked(True) + self.dstListRegexpCheck.setEnabled(True) + self.dstRegexpListsLine.setText(operator.data) + self.selectListRegexpButton.setEnabled(True) + + if operator.operand == Config.OPERAND_LIST_IPS: + self.dstListIPsCheck.setChecked(True) + self.dstListIPsCheck.setEnabled(True) + self.dstListIPsLine.setText(operator.data) + self.selectIPsListButton.setEnabled(True) + + if operator.operand == Config.OPERAND_LIST_NETS: + self.dstListNetsCheck.setChecked(True) + self.dstListNetsCheck.setEnabled(True) + self.dstListNetsLine.setText(operator.data) + self.selectNetsListButton.setEnabled(True) + + def _load_nodes(self, addr=None): + try: + self.nodesCombo.clear() + self._node_list = self._nodes.get() + + if addr != None and addr not in self._node_list: + Message.ok(QC.translate("rules", "<b>Error loading rule</b>"), + QC.translate("rules", "node {0} not connected".format(addr)), + QtWidgets.QMessageBox.Warning) + return False + + if len(self._node_list) < 2: + self.nodeApplyAllCheck.setVisible(False) + + for node in self._node_list: + self.nodesCombo.addItem(node) + + if addr != None: + self.nodesCombo.setCurrentText(addr) + + except Exception as e: + print(self.LOG_TAG, "exception loading nodes: ", e, addr) + return False + + return True + + def _insert_rule_to_db(self, node_addr): + # the order of the fields doesn't matter here, as long as we use the + # name of the field. + self._rules.add_rules(node_addr, [self.rule]) + + def _add_rule(self): + try: + if self.nodeApplyAllCheck.isChecked(): + for pos in range(self.nodesCombo.count()): + self._insert_rule_to_db(self.nodesCombo.itemText(pos)) + else: + self._insert_rule_to_db(self.nodesCombo.currentText()) + + notif = ui_pb2.Notification( + id=int(str(time.time()).replace(".", "")), + type=ui_pb2.CHANGE_RULE, + data="", + rules=[self.rule]) + if self.nodeApplyAllCheck.isChecked(): + nid = self._nodes.send_notifications(notif, self._notification_callback) + else: + nid = self._nodes.send_notification(self.nodesCombo.currentText(), notif, self._notification_callback) + + self._notifications_sent[nid] = notif + except Exception as e: + print(self.LOG_TAG, "add_rule() exception: ", e) + + def _delete_rule(self): + try: + # if the rule name has changed, we need to remove the old one + if self._old_rule_name != self.rule.name: + node = self.nodesCombo.currentText() + old_rule = self.rule + old_rule.name = self._old_rule_name + if self.nodeApplyAllCheck.isChecked(): + nid, noti = self._nodes.delete_rule(rule_name=self._old_rule_name, addr=None, callback=self._notification_callback) + self._notifications_sent[nid] = noti + else: + nid, noti = self._nodes.delete_rule(self._old_rule_name, node, self._notification_callback) + self._notifications_sent[nid] = noti + + except Exception as e: + print(self.LOG_TAG, "delete_rule() exception: ", e) + + + def _save_rule(self): + """ + Create a new rule based on the fields selected. + + Ensure that some constraints are met: + - Determine if a field can be a regexp. + - Validate regexp. + - Fields cannot be empty. + - If the user has not provided a rule name, auto assign one. + """ + self.rule = ui_pb2.Rule() + self.rule.created = int(datetime.now().timestamp()) + self.rule.name = self.ruleNameEdit.text() + self.rule.description = self.ruleDescEdit.toPlainText() + self.rule.enabled = self.enableCheck.isChecked() + self.rule.precedence = self.precedenceCheck.isChecked() + self.rule.nolog = self.nologCheck.isChecked() + self.rule.operator.type = Config.RULE_TYPE_SIMPLE + self.rule.action = Config.ACTION_DENY + if self.actionAllowRadio.isChecked(): + self.rule.action = Config.ACTION_ALLOW + elif self.actionRejectRadio.isChecked(): + self.rule.action = Config.ACTION_REJECT + + self.rule.duration = self._get_duration(self.durationCombo.currentIndex()) + + # FIXME: there should be a sensitive checkbox per operand + self.rule.operator.sensitive = self.sensitiveCheck.isChecked() + rule_data = [] + if self.protoCheck.isChecked(): + if self.protoCombo.currentText() == "": + return False, QC.translate("rules", "protocol can not be empty, or uncheck it") + + self.rule.operator.operand = Config.OPERAND_PROTOCOL + self.rule.operator.data = self.protoCombo.currentText() + rule_data.append( + { + "type": Config.RULE_TYPE_SIMPLE, + "operand": Config.OPERAND_PROTOCOL, + "data": self.protoCombo.currentText().lower(), + "sensitive": self.sensitiveCheck.isChecked() + }) + if self._is_regex(self.protoCombo.currentText()): + rule_data[len(rule_data)-1]['type'] = Config.RULE_TYPE_REGEXP + if self._is_valid_regex(self.protoCombo.currentText()) == False: + return False, QC.translate("rules", "Protocol regexp error") + + if self.procCheck.isChecked(): + if self.procLine.text() == "": + return False, QC.translate("rules", "process path can not be empty") + + self.rule.operator.operand = Config.OPERAND_PROCESS_PATH + self.rule.operator.data = self.procLine.text() + rule_data.append( + { + "type": Config.RULE_TYPE_SIMPLE, + "operand": Config.OPERAND_PROCESS_PATH, + "data": self.procLine.text(), + "sensitive": self.sensitiveCheck.isChecked() + }) + if self.checkProcRegexp.isChecked(): + rule_data[len(rule_data)-1]['type'] = Config.RULE_TYPE_REGEXP + if self._is_valid_regex(self.procLine.text()) == False: + return False, QC.translate("rules", "Process path regexp error") + + if self.cmdlineCheck.isChecked(): + if self.cmdlineLine.text() == "": + return False, QC.translate("rules", "command line can not be empty") + + self.rule.operator.operand = Config.OPERAND_PROCESS_COMMAND + self.rule.operator.data = self.cmdlineLine.text() + rule_data.append( + { + 'type': Config.RULE_TYPE_SIMPLE, + 'operand': Config.OPERAND_PROCESS_COMMAND, + 'data': self.cmdlineLine.text(), + "sensitive": self.sensitiveCheck.isChecked() + }) + if self.checkCmdlineRegexp.isChecked(): + rule_data[len(rule_data)-1]['type'] = Config.RULE_TYPE_REGEXP + if self._is_valid_regex(self.cmdlineLine.text()) == False: + return False, QC.translate("rules", "Command line regexp error") + + if self.ifaceCheck.isChecked(): + if self.ifaceCombo.currentText() == "": + return False, QC.translate("rules", "Network interface can not be empty") + + self.rule.operator.operand = Config.OPERAND_IFACE_OUT + self.rule.operator.data = self.ifaceCombo.currentText() + rule_data.append( + { + 'type': Config.RULE_TYPE_SIMPLE, + 'operand': Config.OPERAND_IFACE_OUT, + 'data': self.ifaceCombo.currentText(), + "sensitive": self.sensitiveCheck.isChecked() + }) + if self._is_regex(self.ifaceCombo.currentText()): + rule_data[len(rule_data)-1]['type'] = Config.RULE_TYPE_REGEXP + if self._is_valid_regex(self.ifaceCombo.currentText()) == False: + return False, QC.translate("rules", "Network interface regexp error") + + if self.srcPortCheck.isChecked(): + if self.srcPortLine.text() == "": + return False, QC.translate("rules", "Source port can not be empty") + + self.rule.operator.operand = Config.OPERAND_SOURCE_PORT + self.rule.operator.data = self.srcPortLine.text() + rule_data.append( + { + 'type': Config.RULE_TYPE_SIMPLE, + 'operand': Config.OPERAND_SOURCE_PORT, + 'data': self.srcPortLine.text(), + "sensitive": self.sensitiveCheck.isChecked() + }) + if self._is_regex(self.srcPortLine.text()): + rule_data[len(rule_data)-1]['type'] = Config.RULE_TYPE_REGEXP + if self._is_valid_regex(self.srcPortLine.text()) == False: + return False, QC.translate("rules", "Source port regexp error") + + if self.dstPortCheck.isChecked(): + if self.dstPortLine.text() == "": + return False, QC.translate("rules", "Dest port can not be empty") + + self.rule.operator.operand = Config.OPERAND_DEST_PORT + self.rule.operator.data = self.dstPortLine.text() + rule_data.append( + { + 'type': Config.RULE_TYPE_SIMPLE, + 'operand': Config.OPERAND_DEST_PORT, + 'data': self.dstPortLine.text(), + "sensitive": self.sensitiveCheck.isChecked() + }) + if self._is_regex(self.dstPortLine.text()): + rule_data[len(rule_data)-1]['type'] = Config.RULE_TYPE_REGEXP + if self._is_valid_regex(self.dstPortLine.text()) == False: + return False, QC.translate("rules", "Dst port regexp error") + + if self.dstHostCheck.isChecked(): + if self.dstHostLine.text() == "": + return False, QC.translate("rules", "Dest host can not be empty") + + self.rule.operator.operand = Config.OPERAND_DEST_HOST + self.rule.operator.data = self.dstHostLine.text() + rule_data.append( + { + 'type': Config.RULE_TYPE_SIMPLE, + 'operand': Config.OPERAND_DEST_HOST, + 'data': self.dstHostLine.text(), + "sensitive": self.sensitiveCheck.isChecked() + }) + if self._is_regex(self.dstHostLine.text()): + rule_data[len(rule_data)-1]['type'] = Config.RULE_TYPE_REGEXP + if self._is_valid_regex(self.dstHostLine.text()) == False: + return False, QC.translate("rules", "Dst host regexp error") + + if self.srcIPCheck.isChecked(): + if self.srcIPCombo.currentText() == "": + return False, QC.translate("rules", "Source IP/Network can not be empty") + + srcIPtext = self.srcIPCombo.currentText() + + if srcIPtext == self.LAN_LABEL: + self.rule.operator.operand = Config.OPERAND_SOURCE_IP + self.rule.operator.type = Config.RULE_TYPE_REGEXP + srcIPtext = self.LAN_RANGES + elif srcIPtext == self.MULTICAST_LABEL: + self.rule.operator.operand = Config.OPERAND_SOURCE_IP + self.rule.operator.type = Config.RULE_TYPE_REGEXP + srcIPtext = self.MULTICAST_RANGE + else: + try: + if type(ipaddress.ip_address(self.srcIPCombo.currentText())) == ipaddress.IPv4Address \ + or type(ipaddress.ip_address(self.srcIPCombo.currentText())) == ipaddress.IPv6Address: + self.rule.operator.operand = Config.OPERAND_SOURCE_IP + self.rule.operator.type = Config.RULE_TYPE_SIMPLE + except Exception: + self.rule.operator.operand = Config.OPERAND_SOURCE_NETWORK + self.rule.operator.type = Config.RULE_TYPE_NETWORK + + if self._is_regex(srcIPtext): + self.rule.operator.operand = Config.OPERAND_SOURCE_IP + self.rule.operator.type = Config.RULE_TYPE_REGEXP + if self._is_valid_regex(self.srcIPCombo.currentText()) == False: + return False, QC.translate("rules", "Source IP regexp error") + + rule_data.append( + { + 'type': self.rule.operator.type, + 'operand': self.rule.operator.operand, + 'data': srcIPtext, + "sensitive": self.sensitiveCheck.isChecked() + }) + + if self.dstIPCheck.isChecked(): + if self.dstIPCombo.currentText() == "": + return False, QC.translate("rules", "Dest IP/Network can not be empty") + + dstIPtext = self.dstIPCombo.currentText() + + if dstIPtext == self.LAN_LABEL: + self.rule.operator.operand = Config.OPERAND_DEST_IP + self.rule.operator.type = Config.RULE_TYPE_REGEXP + dstIPtext = self.LAN_RANGES + elif dstIPtext == self.MULTICAST_LABEL: + self.rule.operator.operand = Config.OPERAND_DEST_IP + self.rule.operator.type = Config.RULE_TYPE_REGEXP + dstIPtext = self.MULTICAST_RANGE + else: + try: + if type(ipaddress.ip_address(self.dstIPCombo.currentText())) == ipaddress.IPv4Address \ + or type(ipaddress.ip_address(self.dstIPCombo.currentText())) == ipaddress.IPv6Address: + self.rule.operator.operand = Config.OPERAND_DEST_IP + self.rule.operator.type = Config.RULE_TYPE_SIMPLE + except Exception: + self.rule.operator.operand = Config.OPERAND_DEST_NETWORK + self.rule.operator.type = Config.RULE_TYPE_NETWORK + + if self._is_regex(dstIPtext): + self.rule.operator.operand = Config.OPERAND_DEST_IP + self.rule.operator.type = Config.RULE_TYPE_REGEXP + if self._is_valid_regex(self.dstIPCombo.currentText()) == False: + return False, QC.translate("rules", "Dst IP regexp error") + + rule_data.append( + { + 'type': self.rule.operator.type, + 'operand': self.rule.operator.operand, + 'data': dstIPtext, + "sensitive": self.sensitiveCheck.isChecked() + }) + + if self.uidCheck.isChecked(): + uidType = Config.RULE_TYPE_SIMPLE + uid = self.uidCombo.currentText() + + if uid == "": + return False, QC.translate("rules", "User ID can not be empty") + + try: + # sometimes when loading a rule, instead of the UID, the format + # "user (uid)" is set. So try to parse it, in order not to save + # a wrong uid. + uidtmp = uid.split(" ") + if len(uidtmp) == 1: + int(uidtmp[0]) + else: + uid = str(pwd.getpwnam(uidtmp[0])[self.PW_UID]) + except: + # if it's not a digit and nor a system user (user (id)), see if + # it's a regexp. + if self._is_regex(self.uidCombo.currentText()): + uidType = Config.RULE_TYPE_REGEXP + if self._is_valid_regex(self.uidCombo.currentText()) == False: + return False, QC.translate("rules", "User ID regexp error") + + else: + return False, QC.translate("rules", "Invalid UID, it must be a digit.") + + self.rule.operator.operand = Config.OPERAND_USER_ID + self.rule.operator.data = self.uidCombo.currentText() + rule_data.append( + { + 'type': uidType, + 'operand': Config.OPERAND_USER_ID, + 'data': uid, + "sensitive": self.sensitiveCheck.isChecked() + }) + + if self.pidCheck.isChecked(): + if self.pidLine.text() == "": + return False, QC.translate("rules", "PID field can not be empty") + + self.rule.operator.operand = Config.OPERAND_PROCESS_ID + self.rule.operator.data = self.pidLine.text() + rule_data.append( + { + 'type': Config.RULE_TYPE_SIMPLE, + 'operand': Config.OPERAND_PROCESS_ID, + 'data': self.pidLine.text(), + "sensitive": self.sensitiveCheck.isChecked() + }) + if self._is_regex(self.pidLine.text()): + rule_data[len(rule_data)-1]['type'] = Config.RULE_TYPE_REGEXP + if self._is_valid_regex(self.pidLine.text()) == False: + return False, QC.translate("rules", "PID field regexp error") + + if self.dstListsCheck.isChecked(): + error = self._is_valid_list_path(self.dstListsLine) + if error: + return False, error + + self.rule.operator.type = Config.RULE_TYPE_LISTS + self.rule.operator.operand = Config.OPERAND_LIST_DOMAINS + rule_data.append( + { + 'type': Config.RULE_TYPE_LISTS, + 'operand': Config.OPERAND_LIST_DOMAINS, + 'data': self.dstListsLine.text(), + 'sensitive': self.sensitiveCheck.isChecked() + }) + self.rule.operator.data = "" + + if self.dstListRegexpCheck.isChecked(): + error = self._is_valid_list_path(self.dstRegexpListsLine) + if error: + return False, error + + self.rule.operator.type = Config.RULE_TYPE_LISTS + self.rule.operator.operand = Config.OPERAND_LIST_DOMAINS_REGEXP + rule_data.append( + { + 'type': Config.RULE_TYPE_LISTS, + 'operand': Config.OPERAND_LIST_DOMAINS_REGEXP, + 'data': self.dstRegexpListsLine.text(), + 'sensitive': self.sensitiveCheck.isChecked() + }) + self.rule.operator.data = "" + + if self.dstListNetsCheck.isChecked(): + error = self._is_valid_list_path(self.dstListNetsLine) + if error: + return False, error + + self.rule.operator.type = Config.RULE_TYPE_LISTS + self.rule.operator.operand = Config.OPERAND_LIST_NETS + rule_data.append( + { + 'type': Config.RULE_TYPE_LISTS, + 'operand': Config.OPERAND_LIST_NETS, + 'data': self.dstListNetsLine.text(), + 'sensitive': self.sensitiveCheck.isChecked() + }) + self.rule.operator.data = "" + + + if self.dstListIPsCheck.isChecked(): + error = self._is_valid_list_path(self.dstListIPsLine) + if error: + return False, error + + self.rule.operator.type = Config.RULE_TYPE_LISTS + self.rule.operator.operand = Config.OPERAND_LIST_IPS + rule_data.append( + { + 'type': Config.RULE_TYPE_LISTS, + 'operand': Config.OPERAND_LIST_IPS, + 'data': self.dstListIPsLine.text(), + 'sensitive': self.sensitiveCheck.isChecked() + }) + self.rule.operator.data = "" + + if len(rule_data) >= 2: + self.rule.operator.type = Config.RULE_TYPE_LIST + self.rule.operator.operand = Config.RULE_TYPE_LIST + self.rule.operator.data = "" + for rd in rule_data: + self.rule.operator.list.extend([ + ui_pb2.Operator( + type=rd['type'], + operand=rd['operand'], + data=rd['data'], + sensitive=rd['sensitive'] + ) + ]) + print(self.rule.operator.list) + + elif len(rule_data) == 1: + self.rule.operator.operand = rule_data[0]['operand'] + self.rule.operator.data = rule_data[0]['data'] + if self.checkProcRegexp.isChecked(): + self.rule.operator.type = Config.RULE_TYPE_REGEXP + elif self.checkCmdlineRegexp.isChecked(): + self.rule.operator.type = Config.RULE_TYPE_REGEXP + elif (self.procCheck.isChecked() == False and self.cmdlineCheck.isChecked() == False) \ + and self._is_regex(self.rule.operator.data): + self.rule.operator.type = Config.RULE_TYPE_REGEXP + + else: + return False, QC.translate("rules", "Select at least one field.") + + if self.ruleNameEdit.text() == "": + self.rule.name = slugify("%s %s %s" % (self.rule.action, self.rule.operator.type, self.rule.operator.data)) + + return True, "" + + def edit_rule(self, records, _addr=None): + self.WORK_MODE = self.EDIT_RULE + self._reset_state() + + self.rule = Rule.new_from_records(records) + if self.rule.operator.type not in Config.RulesTypes: + Message.ok(QC.translate("rules", "<b>Rule not supported</b>"), + QC.translate("rules", "This type of rule ({0}) is not supported by version {1}".format(self.rule.operator.type, version)), + QtWidgets.QMessageBox.Warning) + self.hide() + return + + self._old_rule_name = records.value(RuleFields.Name) + + if self._load_rule(addr=_addr, rule=self.rule): + self.show() + + def new_rule(self): + self.WORK_MODE = self.ADD_RULE + self._reset_state() + self._load_nodes() + self.show() + + def new_rule_from_connection(self, coltime): + self.WORK_MODE = self.ADD_RULE + self._reset_state() + self._load_nodes() + + try: + records = self._db.get_connection_by_field("time", coltime) + if records.next() == False: + print(self.LOG_TAG, "error loading connection fields by time: {0}".format(coltime)) + return False + + self.set_fields_from_connection(records) + self.show() + except Exception as e: + print(self.LOG_TAG, "exception creating new rule from connection:", e) + return False + + return True diff --git a/ui/opensnitch/dialogs/stats.py b/ui/opensnitch/dialogs/stats.py new file mode 100644 index 0000000..26358a7 --- /dev/null +++ b/ui/opensnitch/dialogs/stats.py @@ -0,0 +1,2908 @@ +import threading +import datetime +import sys +import os +import csv +import io + +from PyQt5 import QtCore, QtGui, uic, QtWidgets +from PyQt5.QtCore import QCoreApplication as QC + +from opensnitch.config import Config +from opensnitch.version import version +from opensnitch.nodes import Nodes +from opensnitch.firewall import Firewall, Rules as FwRules +from opensnitch.dialogs.firewall import FirewallDialog +from opensnitch.dialogs.preferences import PreferencesDialog +from opensnitch.dialogs.ruleseditor import RulesEditorDialog +from opensnitch.dialogs.processdetails import ProcessDetailsDialog +from opensnitch.dialogs.conndetails import ConnDetails +from opensnitch.customwidgets.colorizeddelegate import ColorizedDelegate +from opensnitch.customwidgets.firewalltableview import FirewallTableModel +from opensnitch.customwidgets.generictableview import GenericTableModel +from opensnitch.customwidgets.addresstablemodel import AddressTableModel +from opensnitch.utils import Message, QuickHelp, AsnDB, Icons +from opensnitch.utils.xdg import xdg_current_desktop +from opensnitch.actions import Actions +from opensnitch.rules import Rule, Rules + +import opensnitch.proto as proto +ui_pb2, ui_pb2_grpc = proto.import_() + +DIALOG_UI_PATH = "%s/../res/stats.ui" % os.path.dirname(sys.modules[__name__].__file__) +class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]): + + _trigger = QtCore.pyqtSignal(bool, bool) + settings_saved = QtCore.pyqtSignal() + close_trigger = QtCore.pyqtSignal() + _status_changed_trigger = QtCore.pyqtSignal(bool) + _shown_trigger = QtCore.pyqtSignal() + _notification_trigger = QtCore.pyqtSignal(ui_pb2.Notification) + _notification_callback = QtCore.pyqtSignal(ui_pb2.NotificationReply) + + SORT_ORDER = ["ASC", "DESC"] + LIMITS = ["LIMIT 50", "LIMIT 100", "LIMIT 200", "LIMIT 300", ""] + LAST_GROUP_BY = "" + + # general + COL_TIME = 0 + COL_NODE = 1 + COL_ACTION = 2 + COL_SRCPORT = 3 + COL_SRCIP = 4 + COL_DSTIP = 5 + COL_DSTHOST = 6 + COL_DSTPORT = 7 + COL_PROTO = 8 + COL_UID = 9 + COL_PID = 10 + COL_PROCS = 11 + COL_CMDLINE = 12 + COL_RULES = 13 + # total number of columns: cols + 1 + GENERAL_COL_NUM = 14 + + # stats + COL_WHAT = 0 + + # rules + COL_R_NODE = 1 + COL_R_NAME = 2 + COL_R_ENABLED = 3 + COL_R_ACTION = 4 + COL_R_DURATION = 5 + COL_R_OP_TYPE = 6 + COL_R_OP_OPERAND = 7 + COL_R_CREATED = 8 + + # procs + COL_PROC_PID = 11 + + TAB_MAIN = 0 + TAB_NODES = 1 + TAB_RULES = 2 + TAB_HOSTS = 3 + TAB_PROCS = 4 + TAB_ADDRS = 5 + TAB_PORTS = 6 + TAB_USERS = 7 + TAB_FIREWALL = 8 + + # tree's top level items + RULES_TREE_APPS = 0 + RULES_TREE_NODES = 1 + RULES_TREE_FIREWALL = 2 + + RULES_TREE_PERMANENT = 0 + RULES_TREE_TEMPORARY = 1 + + RULES_COMBO_PERMANENT = 1 + RULES_COMBO_TEMPORARY = 2 + RULES_COMBO_FW = 3 + + RULES_TYPE_PERMANENT = 0 + RULES_TYPE_TEMPORARY = 1 + + FILTER_TREE_APPS = 0 + FILTER_TREE_NODES = 3 + + FILTER_TREE_FW_NODE = 0 + FILTER_TREE_FW_TABLE = 1 + FILTER_TREE_FW_CHAIN = 2 + + # FIXME: don't translate, used only for default argument on _update_status_label + FIREWALL_DISABLED = "Disabled" + + # if the user clicks on an item of a table, it'll enter into the detail + # view. From there, deny further clicks on the items. + IN_DETAIL_VIEW = { + TAB_MAIN: False, + TAB_NODES: False, + TAB_RULES: False, + TAB_HOSTS: False, + TAB_PROCS: False, + TAB_ADDRS: False, + TAB_PORTS: False, + TAB_USERS: False, + TAB_FIREWALL: False + } + # restore scrollbar position when going back from a detail view + LAST_SCROLL_VALUE = None + # try to restore last selection + LAST_SELECTED_ITEM = "" + + TABLES = { + TAB_MAIN: { + "name": "connections", + "label": None, + "cmd": None, + "cmdCleanStats": None, + "view": None, + "filterLine": None, + "model": None, + "delegate": "commonDelegateConfig", + "display_fields": "time as Time, " \ + "node, " \ + "action, " \ + "src_port, " \ + "src_ip, " \ + "dst_ip, " \ + "dst_host, " \ + "dst_port, " \ + "protocol, " \ + "uid, " \ + "pid, " \ + "process, " \ + "process_args, " \ + "rule", + "group_by": LAST_GROUP_BY, + "last_order_by": "1", + "last_order_to": 1, + "tracking_column:": COL_TIME + }, + TAB_NODES: { + "name": "nodes", + "label": None, + "cmd": None, + "cmdCleanStats": None, + "view": None, + "filterLine": None, + "model": None, + "delegate": "commonDelegateConfig", + "display_fields": "last_connection as LastConnection, "\ + "addr as Addr, " \ + "status as Status, " \ + "hostname as Hostname, " \ + "daemon_version as Version, " \ + "daemon_uptime as Uptime, " \ + "daemon_rules as Rules," \ + "cons as Connections," \ + "cons_dropped as Dropped," \ + "version as Version", + "header_labels": [], + "last_order_by": "1", + "last_order_to": 1, + "tracking_column:": COL_TIME + }, + TAB_RULES: { + "name": "rules", + "label": None, + "cmd": None, + "cmdCleanStats": None, + "view": None, + "filterLine": None, + "model": None, + "delegate": "defaultRulesDelegateConfig", + "display_fields": "time as Time," \ + "node as Node," \ + "name as Name," \ + "enabled as Enabled," \ + "action as Action," \ + "duration as Duration," \ + "description as Description, " \ + "created as Created", + "header_labels": [], + "last_order_by": "2", + "last_order_to": 0, + "tracking_column:": COL_R_NAME + }, + TAB_FIREWALL: { + "name": "firewall", + "label": None, + "cmd": None, + "cmdCleanStats": None, + "view": None, + "filterLine": None, + "model": None, + "delegate": "defaultFWDelegateConfig", + "display_fields": "*", + "header_labels": [], + "last_order_by": "2", + "last_order_to": 0, + "tracking_column:": COL_TIME + }, + TAB_HOSTS: { + "name": "hosts", + "label": None, + "cmd": None, + "cmdCleanStats": None, + "view": None, + "filterLine": None, + "model": None, + "delegate": "commonDelegateConfig", + "display_fields": "*", + "header_labels": [], + "last_order_by": "2", + "last_order_to": 1, + "tracking_column:": COL_TIME + }, + TAB_PROCS: { + "name": "procs", + "label": None, + "cmd": None, + "cmdCleanStats": None, + "view": None, + "filterLine": None, + "model": None, + "delegate": "commonDelegateConfig", + "display_fields": "*", + "header_labels": [], + "last_order_by": "2", + "last_order_to": 1, + "tracking_column:": COL_TIME + }, + TAB_ADDRS: { + "name": "addrs", + "label": None, + "cmd": None, + "cmdCleanStats": None, + "view": None, + "filterLine": None, + "model": None, + "delegate": "commonDelegateConfig", + "display_fields": "*", + "header_labels": [], + "last_order_by": "2", + "last_order_to": 1, + "tracking_column:": COL_TIME + }, + TAB_PORTS: { + "name": "ports", + "label": None, + "cmd": None, + "cmdCleanStats": None, + "view": None, + "filterLine": None, + "model": None, + "delegate": "commonDelegateConfig", + "display_fields": "*", + "header_labels": [], + "last_order_by": "2", + "last_order_to": 1, + "tracking_column:": COL_TIME + }, + TAB_USERS: { + "name": "users", + "label": None, + "cmd": None, + "cmdCleanStats": None, + "view": None, + "filterLine": None, + "model": None, + "delegate": "commonDelegateConfig", + "display_fields": "*", + "header_labels": [], + "last_order_by": "2", + "last_order_to": 1, + "tracking_column:": COL_TIME + } + } + + def __init__(self, parent=None, address=None, db=None, dbname="db", appicon=None): + super(StatsDialog, self).__init__(parent) + + self.setWindowFlags(QtCore.Qt.Window) + self.setupUi(self) + self.setWindowIcon(appicon) + + # columns names. Must be added here in order to names be translated. + self.COL_STR_NAME = QC.translate("stats", "Name", "This is a word, without spaces and symbols.").replace(" ", "") + self.COL_STR_ADDR = QC.translate("stats", "Address", "This is a word, without spaces and symbols.").replace(" ", "") + self.COL_STR_STATUS = QC.translate("stats", "Status", "This is a word, without spaces and symbols.").replace(" ", "") + self.COL_STR_HOSTNAME = QC.translate("stats", "Hostname", "This is a word, without spaces and symbols.").replace(" ", "") + self.COL_STR_UPTIME = QC.translate("stats", "Uptime", "This is a word, without spaces and symbols.").replace(" ", "") + self.COL_STR_VERSION = QC.translate("stats", "Version", "This is a word, without spaces and symbols.").replace(" ", "") + self.COL_STR_RULES_NUM = QC.translate("stats", "Rules", "This is a word, without spaces and symbols.").replace(" ", "") + self.COL_STR_TIME = QC.translate("stats", "Time", "This is a word, without spaces and symbols.").replace(" ", "") + self.COL_STR_CREATED = QC.translate("stats", "Created", "This is a word, without spaces and symbols.").replace(" ", "") + self.COL_STR_ACTION = QC.translate("stats", "Action", "This is a word, without spaces and symbols.").replace(" ", "") + self.COL_STR_DURATION = QC.translate("stats", "Duration", "This is a word, without spaces and symbols.").replace(" ", "") + self.COL_STR_DESCRIPTION = QC.translate("stats", "Description", "This is a word, without spaces and symbols.").replace(" ", "") + self.COL_STR_NODE = QC.translate("stats", "Node", "This is a word, without spaces and symbols.").replace(" ", "") + self.COL_STR_ENABLED = QC.translate("stats", "Enabled", "This is a word, without spaces and symbols.").replace(" ", "") + self.COL_STR_PRECEDENCE = QC.translate("stats", "Precedence", "This is a word, without spaces and symbols.").replace(" ", "") + self.COL_STR_HITS = QC.translate("stats", "Hits", "This is a word, without spaces and symbols.").replace(" ", "") + self.COL_STR_PROTOCOL = QC.translate("stats", "Protocol", "This is a word, without spaces and symbols.").replace(" ", "") + self.COL_STR_PROCESS = QC.translate("stats", "Process", "This is a word, without spaces and symbols.").replace(" ", "") + self.COL_STR_PROC_CMDLINE = QC.translate("stats", "Cmdline", "This is a word, without spaces and symbols.").replace(" ", "") + self.COL_STR_DESTINATION = QC.translate("stats", "Destination", "This is a word, without spaces and symbols.").replace(" ", "") + self.COL_STR_SRC_PORT = QC.translate("stats", "SrcPort", "This is a word, without spaces and symbols.").replace(" ", "") + self.COL_STR_SRC_IP = QC.translate("stats", "SrcIP", "This is a word, without spaces and symbols.").replace(" ", "") + self.COL_STR_DST_IP = QC.translate("stats", "DstIP", "This is a word, without spaces and symbols.").replace(" ", "") + self.COL_STR_DST_HOST = QC.translate("stats", "DstHost", "This is a word, without spaces and symbols.").replace(" ", "") + self.COL_STR_DST_PORT = QC.translate("stats", "DstPort", "This is a word, without spaces and symbols.").replace(" ", "") + self.COL_STR_RULE = QC.translate("stats", "Rule", "This is a word, without spaces and symbols.").replace(" ", "") + self.COL_STR_UID = QC.translate("stats", "UserID", "This is a word, without spaces and symbols.").replace(" ", "") + self.COL_STR_PID = QC.translate("stats", "PID", "This is a word, without spaces and symbols.").replace(" ", "") + self.COL_STR_LAST_CONNECTION = QC.translate("stats", "LastConnection", "This is a word, without spaces and symbols.").replace(" ", "") + + self.FIREWALL_STOPPED = QC.translate("stats", "Not running") + self.FIREWALL_DISABLED = QC.translate("stats", "Disabled") + self.FIREWALL_RUNNING = QC.translate("stats", "Running") + + self._db = db + self._db_sqlite = self._db.get_db() + self._db_name = dbname + + self.asndb = AsnDB.instance() + + self._cfg = Config.get() + self._nodes = Nodes.instance() + self._fw = Firewall().instance() + self._rules = Rules.instance() + self._fw.rules.rulesUpdated.connect(self._cb_fw_rules_updated) + self._rules.updated.connect(self._cb_app_rules_updated) + self._actions = Actions().instance() + self._actions.loadAll() + self._last_update = datetime.datetime.now() + + # TODO: allow to display multiples dialogs + self._proc_details_dialog = ProcessDetailsDialog(appicon=appicon) + # TODO: allow to navigate records by offsets + self.prevButton.setVisible(False) + self.nextButton.setVisible(False) + + + self.fwTable.setVisible(False) + self.rulesTable.setVisible(True) + + self.daemon_connected = False + # skip table updates if a context menu is active + self._context_menu_active = False + # used to skip updates while the user is moving the scrollbar + self.scrollbar_active = False + + self._lock = threading.RLock() + self._address = address + self._stats = None + self._notifications_sent = {} + + self._fw_dialog = FirewallDialog(appicon=appicon) + self._prefs_dialog = PreferencesDialog(appicon=appicon) + self._rules_dialog = RulesEditorDialog(appicon=appicon) + self._prefs_dialog.saved.connect(self._on_settings_saved) + self._trigger.connect(self._on_update_triggered) + self._notification_callback.connect(self._cb_notification_callback) + + self.nodeLabel.setText("") + self.nodeLabel.setStyleSheet('color: green;font-size:12pt; font-weight:600;') + self.rulesSplitter.setStretchFactor(0,0) + self.rulesSplitter.setStretchFactor(1,2) + self.rulesTreePanel.resizeColumnToContents(0) + self.rulesTreePanel.resizeColumnToContents(1) + self.rulesTreePanel.itemExpanded.connect(self._cb_rules_tree_item_expanded) + + self.startButton.clicked.connect(self._cb_start_clicked) + self.nodeStartButton.clicked.connect(self._cb_node_start_clicked) + self.nodeStartButton.setVisible(False) + self.nodePrefsButton.setVisible(False) + self.nodeActionsButton.setVisible(False) + self.nodeDeleteButton.setVisible(False) + self.nodeDeleteButton.clicked.connect(self._cb_node_delete_clicked) + self.prefsButton.clicked.connect(self._cb_prefs_clicked) + self.nodePrefsButton.clicked.connect(self._cb_node_prefs_clicked) + self.fwButton.clicked.connect(lambda: self._fw_dialog.show()) + self.comboAction.currentIndexChanged.connect(self._cb_combo_action_changed) + self.limitCombo.currentIndexChanged.connect(self._cb_limit_combo_changed) + self.tabWidget.currentChanged.connect(self._cb_tab_changed) + self.delRuleButton.clicked.connect(self._cb_del_rule_clicked) + self.rulesSplitter.splitterMoved.connect(self._cb_rules_splitter_moved) + self.rulesTreePanel.itemClicked.connect(self._cb_rules_tree_item_clicked) + self.rulesTreePanel.itemDoubleClicked.connect(self._cb_rules_tree_item_double_clicked) + self.enableRuleCheck.clicked.connect(self._cb_enable_rule_toggled) + self.editRuleButton.clicked.connect(self._cb_edit_rule_clicked) + self.newRuleButton.clicked.connect(self._cb_new_rule_clicked) + self.cmdProcDetails.clicked.connect(self._cb_proc_details_clicked) + self.comboRulesFilter.currentIndexChanged.connect(self._cb_rules_filter_combo_changed) + self.helpButton.clicked.connect(self._cb_help_button_clicked) + self.nextButton.clicked.connect(self._cb_next_button_clicked) + self.prevButton.clicked.connect(self._cb_prev_button_clicked) + + self.enableRuleCheck.setVisible(False) + self.delRuleButton.setVisible(False) + self.editRuleButton.setVisible(False) + self.nodeRuleLabel.setVisible(False) + self.comboRulesFilter.setVisible(False) + + menu = QtWidgets.QMenu() + menu.addAction(Icons.new(self, "go-up"), QC.translate("stats", "Export rules")).triggered.connect(self._on_menu_node_export_clicked) + menu.addAction(Icons.new(self, "go-down"), QC.translate("stats", "Import rules")).triggered.connect(self._on_menu_node_import_clicked) + self.nodeActionsButton.setMenu(menu) + + menuActions = QtWidgets.QMenu() + menuActions.addAction(Icons.new(self, "go-up"), QC.translate("stats", "Export rules")).triggered.connect(self._on_menu_export_clicked) + menuActions.addAction(Icons.new(self, "go-down"), QC.translate("stats", "Import rules")).triggered.connect(self._on_menu_import_clicked) + menuActions.addAction(Icons.new(self, "document-save"), QC.translate("stats", "Export events to CSV")).triggered.connect(self._on_menu_export_csv_clicked) + menuActions.addAction(Icons.new(self, "application-exit"), QC.translate("stats", "Quit")).triggered.connect(self._on_menu_exit_clicked) + self.actionsButton.setMenu(menuActions) + + # translations must be done here, otherwise they don't take effect + self.TABLES[self.TAB_NODES]['header_labels'] = [ + self.COL_STR_LAST_CONNECTION, + self.COL_STR_ADDR, + self.COL_STR_STATUS, + self.COL_STR_HOSTNAME, + self.COL_STR_VERSION, + self.COL_STR_UPTIME, + QC.translate("stats", "Rules", "This is a word, without spaces and symbols.").replace(" ", ""), + QC.translate("stats", "Connections", "This is a word, without spaces and symbols.").replace(" ", ""), + QC.translate("stats", "Dropped", "This is a word, without spaces and symbols.").replace(" ", ""), + QC.translate("stats", "Version", "This is a word, without spaces and symbols.").replace(" ", ""), + ] + + self.TABLES[self.TAB_RULES]['header_labels'] = [ + self.COL_STR_TIME, + self.COL_STR_NODE, + self.COL_STR_NAME, + self.COL_STR_ENABLED, + self.COL_STR_ACTION, + self.COL_STR_DURATION, + self.COL_STR_DESCRIPTION, + self.COL_STR_CREATED, + ] + + stats_headers = [ + QC.translate("stats", "What", "This is a word, without spaces and symbols.").replace(" ", ""), + QC.translate("stats", "Hits", "This is a word, without spaces and symbols.").replace(" ", ""), + ] + + self.TABLES[self.TAB_HOSTS]['header_labels'] = stats_headers + self.TABLES[self.TAB_PROCS]['header_labels'] = stats_headers + self.TABLES[self.TAB_ADDRS]['header_labels'] = stats_headers + self.TABLES[self.TAB_PORTS]['header_labels'] = stats_headers + self.TABLES[self.TAB_USERS]['header_labels'] = stats_headers + + self.TABLES[self.TAB_MAIN]['view'] = self._setup_table(QtWidgets.QTableView, self.eventsTable, "connections", + self.TABLES[self.TAB_MAIN]['display_fields'], + order_by="1", + group_by=self.TABLES[self.TAB_MAIN]['group_by'], + delegate=self.TABLES[self.TAB_MAIN]['delegate'], + resize_cols=(), + model=GenericTableModel("connections", [ + self.COL_STR_TIME, + self.COL_STR_NODE, + self.COL_STR_ACTION, + self.COL_STR_SRC_PORT, + self.COL_STR_SRC_IP, + self.COL_STR_DST_IP, + self.COL_STR_DST_HOST, + self.COL_STR_DST_PORT, + self.COL_STR_PROTOCOL, + self.COL_STR_UID, + self.COL_STR_PID, + self.COL_STR_PROCESS, + self.COL_STR_PROC_CMDLINE, + self.COL_STR_RULE, + ]), + verticalScrollBar=self.connectionsTableScrollBar, + limit=self._get_limit() + ) + self.TABLES[self.TAB_NODES]['view'] = self._setup_table(QtWidgets.QTableView, self.nodesTable, "nodes", + self.TABLES[self.TAB_NODES]['display_fields'], + order_by="3,2,1", + resize_cols=(self.COL_NODE,), + model=GenericTableModel("nodes", self.TABLES[self.TAB_NODES]['header_labels']), + verticalScrollBar=self.verticalScrollBar, + sort_direction=self.SORT_ORDER[1], + delegate=self.TABLES[self.TAB_NODES]['delegate']) + self.TABLES[self.TAB_RULES]['view'] = self._setup_table(QtWidgets.QTableView, self.rulesTable, "rules", + fields=self.TABLES[self.TAB_RULES]['display_fields'], + model=GenericTableModel("rules", self.TABLES[self.TAB_RULES]['header_labels']), + verticalScrollBar=self.rulesScrollBar, + delegate=self.TABLES[self.TAB_RULES]['delegate'], + order_by="2", + sort_direction=self.SORT_ORDER[0], + tracking_column=self.COL_R_NAME) + self.TABLES[self.TAB_FIREWALL]['view'] = self._setup_table(QtWidgets.QTableView, + self.fwTable, "firewall", + model=FirewallTableModel("firewall"), + verticalScrollBar=None, + delegate=self.TABLES[self.TAB_FIREWALL]['delegate'], + order_by="2", + sort_direction=self.SORT_ORDER[0]) + self.TABLES[self.TAB_HOSTS]['view'] = self._setup_table(QtWidgets.QTableView, + self.hostsTable, "hosts", + model=GenericTableModel("hosts", self.TABLES[self.TAB_HOSTS]['header_labels']), + verticalScrollBar=self.hostsScrollBar, + resize_cols=(self.COL_WHAT,), + delegate=self.TABLES[self.TAB_HOSTS]['delegate'], + order_by="2", + limit=self._get_limit() + ) + self.TABLES[self.TAB_PROCS]['view'] = self._setup_table(QtWidgets.QTableView, + self.procsTable, "procs", + model=GenericTableModel("procs", self.TABLES[self.TAB_PROCS]['header_labels']), + verticalScrollBar=self.procsScrollBar, + resize_cols=(self.COL_WHAT,), + delegate=self.TABLES[self.TAB_PROCS]['delegate'], + order_by="2", + limit=self._get_limit() + ) + self.TABLES[self.TAB_ADDRS]['view'] = self._setup_table(QtWidgets.QTableView, + self.addrTable, "addrs", + model=AddressTableModel("addrs", self.TABLES[self.TAB_ADDRS]['header_labels']), + verticalScrollBar=self.addrsScrollBar, + resize_cols=(self.COL_WHAT,), + delegate=self.TABLES[self.TAB_ADDRS]['delegate'], + order_by="2", + limit=self._get_limit() + ) + self.TABLES[self.TAB_PORTS]['view'] = self._setup_table(QtWidgets.QTableView, + self.portsTable, "ports", + model=GenericTableModel("ports", self.TABLES[self.TAB_PORTS]['header_labels']), + verticalScrollBar=self.portsScrollBar, + resize_cols=(self.COL_WHAT,), + delegate=self.TABLES[self.TAB_PORTS]['delegate'], + order_by="2", + limit=self._get_limit() + ) + self.TABLES[self.TAB_USERS]['view'] = self._setup_table(QtWidgets.QTableView, + self.usersTable, "users", + model=GenericTableModel("users", self.TABLES[self.TAB_USERS]['header_labels']), + verticalScrollBar=self.usersScrollBar, + resize_cols=(self.COL_WHAT,), + delegate=self.TABLES[self.TAB_USERS]['delegate'], + order_by="2", + limit=self._get_limit() + ) + + self.TABLES[self.TAB_NODES]['label'] = self.nodesLabel + self.TABLES[self.TAB_RULES]['label'] = self.ruleLabel + self.TABLES[self.TAB_HOSTS]['label'] = self.hostsLabel + self.TABLES[self.TAB_PROCS]['label'] = self.procsLabel + self.TABLES[self.TAB_ADDRS]['label'] = self.addrsLabel + self.TABLES[self.TAB_PORTS]['label'] = self.portsLabel + self.TABLES[self.TAB_USERS]['label'] = self.usersLabel + + self.TABLES[self.TAB_NODES]['cmd'] = self.cmdNodesBack + self.TABLES[self.TAB_RULES]['cmd'] = self.cmdRulesBack + self.TABLES[self.TAB_HOSTS]['cmd'] = self.cmdHostsBack + self.TABLES[self.TAB_PROCS]['cmd'] = self.cmdProcsBack + self.TABLES[self.TAB_ADDRS]['cmd'] = self.cmdAddrsBack + self.TABLES[self.TAB_PORTS]['cmd'] = self.cmdPortsBack + self.TABLES[self.TAB_USERS]['cmd'] = self.cmdUsersBack + + self.TABLES[self.TAB_MAIN]['cmdCleanStats'] = self.cmdCleanSql + self.TABLES[self.TAB_NODES]['cmdCleanStats'] = self.cmdCleanSql + self.TABLES[self.TAB_RULES]['cmdCleanStats'] = self.cmdCleanSql + self.TABLES[self.TAB_HOSTS]['cmdCleanStats'] = self.cmdCleanSql + self.TABLES[self.TAB_PROCS]['cmdCleanStats'] = self.cmdCleanSql + self.TABLES[self.TAB_ADDRS]['cmdCleanStats'] = self.cmdCleanSql + self.TABLES[self.TAB_PORTS]['cmdCleanStats'] = self.cmdCleanSql + self.TABLES[self.TAB_USERS]['cmdCleanStats'] = self.cmdCleanSql + # the rules clean button is only for a particular rule, not all. + self.TABLES[self.TAB_MAIN]['cmdCleanStats'].clicked.connect(lambda: self._cb_clean_sql_clicked(self.TAB_MAIN)) + + self.TABLES[self.TAB_MAIN]['filterLine'] = self.filterLine + self.TABLES[self.TAB_MAIN]['view'].doubleClicked.connect(self._cb_main_table_double_clicked) + self.TABLES[self.TAB_MAIN]['view'].installEventFilter(self) + self.TABLES[self.TAB_MAIN]['filterLine'].textChanged.connect(self._cb_events_filter_line_changed) + + self.TABLES[self.TAB_MAIN]['view'].setContextMenuPolicy(QtCore.Qt.CustomContextMenu) + self.TABLES[self.TAB_MAIN]['view'].customContextMenuRequested.connect(self._cb_table_context_menu) + self.TABLES[self.TAB_RULES]['view'].setContextMenuPolicy(QtCore.Qt.CustomContextMenu) + self.TABLES[self.TAB_RULES]['view'].customContextMenuRequested.connect(self._cb_table_context_menu) + self.TABLES[self.TAB_FIREWALL]['view'].setContextMenuPolicy(QtCore.Qt.CustomContextMenu) + self.TABLES[self.TAB_FIREWALL]['view'].customContextMenuRequested.connect(self._cb_table_context_menu) + for idx in range(1,9): + if self.TABLES[idx]['cmd'] != None: + self.TABLES[idx]['cmd'].hide() + self.TABLES[idx]['cmd'].setVisible(False) + self.TABLES[idx]['cmd'].clicked.connect(lambda: self._cb_cmd_back_clicked(idx)) + if self.TABLES[idx]['cmdCleanStats'] != None: + self.TABLES[idx]['cmdCleanStats'].clicked.connect(lambda: self._cb_clean_sql_clicked(idx)) + if self.TABLES[idx]['label'] != None: + self.TABLES[idx]['label'].setStyleSheet('font-weight:600;') + self.TABLES[idx]['label'].setVisible(False) + self.TABLES[idx]['view'].doubleClicked.connect(self._cb_table_double_clicked) + self.TABLES[idx]['view'].installEventFilter(self) + + self.TABLES[self.TAB_FIREWALL]['view'].rowsReordered.connect(self._cb_fw_table_rows_reordered) + + self._load_settings() + + self._tables = ( \ + self.TABLES[self.TAB_MAIN]['view'], + self.TABLES[self.TAB_NODES]['view'], + self.TABLES[self.TAB_RULES]['view'], + self.TABLES[self.TAB_HOSTS]['view'], + self.TABLES[self.TAB_PROCS]['view'], + self.TABLES[self.TAB_ADDRS]['view'], + self.TABLES[self.TAB_PORTS]['view'], + self.TABLES[self.TAB_USERS]['view'] + ) + self._file_names = ( \ + 'events.csv', + 'nodes.csv', + 'rules.csv', + 'hosts.csv', + 'procs.csv', + 'addrs.csv', + 'ports.csv', + 'users.csv' + ) + + self.iconStart = Icons.new(self, "media-playback-start") + self.iconPause = Icons.new(self, "media-playback-pause") + + self.fwTreeEdit = QtWidgets.QPushButton() + self.fwTreeEdit.setIcon(QtGui.QIcon().fromTheme("preferences-desktop")) + self.fwTreeEdit.autoFillBackground = True + self.fwTreeEdit.setFlat(True) + self.fwTreeEdit.setSizePolicy( + QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed) + ) + self.fwTreeEdit.clicked.connect(self._cb_tree_edit_firewall_clicked) + self._configure_buttons_icons() + + #Sometimes a maximized window which had been minimized earlier won't unminimize + #To workaround, we explicitely maximize such windows when unminimizing happens + def changeEvent(self, event): + if event.type() == QtCore.QEvent.WindowStateChange: + if event.oldState() & QtCore.Qt.WindowMinimized and event.oldState() & QtCore.Qt.WindowMaximized: + #a previously minimized maximized window ... + if self.windowState() ^ QtCore.Qt.WindowMinimized and xdg_current_desktop == "KDE": + # is not minimized anymore, i.e. it was unminimized + # docs: https://doc.qt.io/qt-5/qwidget.html#setWindowState + self.setWindowState(self.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive) + + def showEvent(self, event): + super(StatsDialog, self).showEvent(event) + self._shown_trigger.emit() + window_title = QC.translate("stats", "OpenSnitch Network Statistics {0}").format(version) + if self._address is not None: + window_title = QC.translate("stats", "OpenSnitch Network Statistics for {0}").format(self._address) + self.nodeLabel.setText(self._address) + self._load_settings() + self._add_rulesTree_nodes() + self._add_rulesTree_fw_chains() + self.setWindowTitle(window_title) + self._refresh_active_table() + self._show_columns() + + def eventFilter(self, source, event): + if event.type() == QtCore.QEvent.KeyPress: + if event.matches(QtGui.QKeySequence.Copy): + self._copy_selected_rows() + return True + elif event.key() == QtCore.Qt.Key_Delete: + table = self._get_active_table() + selection = table.selectionModel().selectedRows() + if selection: + model = table.model() + self._table_menu_delete(self.tabWidget.currentIndex(), model, selection) + # we need to manually refresh the model + table.selectionModel().clear() + self._refresh_active_table() + return True + return super(StatsDialog, self).eventFilter(source, event) + + def _configure_buttons_icons(self): + newRuleIcon = Icons.new(self, "document-new") + delRuleIcon = Icons.new(self, "edit-delete") + editRuleIcon = Icons.new(self, "accessories-text-editor") + prefsIcon = Icons.new(self, "preferences-system") + searchIcon = Icons.new(self, "system-search") + clearIcon = Icons.new(self, "edit-clear-all") + leftArrowIcon = Icons.new(self, "go-previous") + fwIcon = Icons.new(self, "security-high") + optsIcon = Icons.new(self, "format-justify-fill") + helpIcon = Icons.new(self, "help-browser") + eventsIcon = Icons.new(self, "view-sort-ascending") + rulesIcon = Icons.new(self, "address-book-new") + procsIcon = Icons.new(self, "system-run") + + if QtGui.QIcon().hasThemeIcon("preferences-desktop") == False: + self.fwTreeEdit.setText("+") + + self.tabWidget.setTabIcon(self.TAB_MAIN, eventsIcon) + self.tabWidget.setTabIcon(self.TAB_RULES, rulesIcon) + self.tabWidget.setTabIcon(self.TAB_PROCS, procsIcon) + self.newRuleButton.setIcon(newRuleIcon) + self.delRuleButton.setIcon(delRuleIcon) + self.editRuleButton.setIcon(editRuleIcon) + self.prefsButton.setIcon(prefsIcon) + self.helpButton.setIcon(helpIcon) + self.startButton.setIcon(self.iconStart) + self.fwButton.setIcon(fwIcon) + self.cmdProcDetails.setIcon(searchIcon) + self.nodeStartButton.setIcon(self.iconStart) + self.nodePrefsButton.setIcon(prefsIcon) + self.nodeDeleteButton.setIcon(clearIcon) + self.nodeActionsButton.setIcon(optsIcon) + self.actionsButton.setIcon(optsIcon) + self.TABLES[self.TAB_MAIN]['cmdCleanStats'].setIcon(clearIcon) + for idx in range(1,8): + self.TABLES[idx]['cmd'].setIcon(leftArrowIcon) + if self.TABLES[idx]['cmdCleanStats'] != None: + self.TABLES[idx]['cmdCleanStats'].setIcon(clearIcon) + + def _load_settings(self): + self._ui_refresh_interval = self._cfg.getInt(Config.STATS_REFRESH_INTERVAL, 0) + dialog_geometry = self._cfg.getSettings(Config.STATS_GEOMETRY) + dialog_last_tab = self._cfg.getSettings(Config.STATS_LAST_TAB) + dialog_general_filter_text = self._cfg.getSettings(Config.STATS_FILTER_TEXT) + dialog_general_filter_action = self._cfg.getSettings(Config.STATS_FILTER_ACTION) + dialog_general_limit_results = self._cfg.getSettings(Config.STATS_LIMIT_RESULTS) + if dialog_geometry != None: + self.restoreGeometry(dialog_geometry) + if dialog_last_tab != None: + self.tabWidget.setCurrentIndex(int(dialog_last_tab)) + if dialog_general_filter_action != None: + self.comboAction.setCurrentIndex(int(dialog_general_filter_action)) + if dialog_general_limit_results != None: + # XXX: a little hack, because if the saved index is 0, the signal is not fired. + # XXX: this causes to fire the event twice + self.limitCombo.blockSignals(True); + self.limitCombo.setCurrentIndex(4) + self.limitCombo.setCurrentIndex(int(dialog_general_limit_results)) + self.limitCombo.blockSignals(False); + + rules_splitter_pos = self._cfg.getSettings(Config.STATS_RULES_SPLITTER_POS) + if type(rules_splitter_pos) == QtCore.QByteArray: + self.rulesSplitter.restoreState(rules_splitter_pos) + rulesSizes = self.rulesSplitter.sizes() + if self.IN_DETAIL_VIEW[self.TAB_RULES] == True: + self.comboRulesFilter.setVisible(False) + elif len(rulesSizes) > 0: + self.comboRulesFilter.setVisible(rulesSizes[0] == 0) + else: + w = self.rulesSplitter.width() + self.rulesSplitter.setSizes([int(w/3), int(w/2)]) + + self._restore_details_view_columns(self.eventsTable.horizontalHeader(), Config.STATS_GENERAL_COL_STATE) + self._restore_details_view_columns(self.nodesTable.horizontalHeader(), Config.STATS_NODES_COL_STATE) + self._restore_details_view_columns(self.rulesTable.horizontalHeader(), Config.STATS_RULES_COL_STATE) + self._restore_details_view_columns(self.fwTable.horizontalHeader(), Config.STATS_FW_COL_STATE) + + rulesTreeNodes_expanded = self._cfg.getBool(Config.STATS_RULES_TREE_EXPANDED_1) + if rulesTreeNodes_expanded != None: + rules_tree_nodes = self._get_rulesTree_item(self.RULES_TREE_NODES) + if rules_tree_nodes != None: + rules_tree_nodes.setExpanded(rulesTreeNodes_expanded) + rulesTreeApps_expanded = self._cfg.getBool(Config.STATS_RULES_TREE_EXPANDED_0) + if rulesTreeApps_expanded != None: + rules_tree_apps = self._get_rulesTree_item(self.RULES_TREE_APPS) + if rules_tree_apps != None: + rules_tree_apps.setExpanded(rulesTreeApps_expanded) + + if dialog_general_filter_text != None: + self.filterLine.setText(dialog_general_filter_text) + + def _save_settings(self): + self._cfg.setSettings(Config.STATS_GEOMETRY, self.saveGeometry()) + self._cfg.setSettings(Config.STATS_LAST_TAB, self.tabWidget.currentIndex()) + self._cfg.setSettings(Config.STATS_LIMIT_RESULTS, self.limitCombo.currentIndex()) + self._cfg.setSettings(Config.STATS_FILTER_TEXT, self.filterLine.text()) + + header = self.eventsTable.horizontalHeader() + self._cfg.setSettings(Config.STATS_GENERAL_COL_STATE, header.saveState()) + nodesHeader = self.nodesTable.horizontalHeader() + self._cfg.setSettings(Config.STATS_NODES_COL_STATE, nodesHeader.saveState()) + rulesHeader = self.rulesTable.horizontalHeader() + self._cfg.setSettings(Config.STATS_RULES_COL_STATE, rulesHeader.saveState()) + fwHeader = self.fwTable.horizontalHeader() + self._cfg.setSettings(Config.STATS_FW_COL_STATE, fwHeader.saveState()) + + rules_tree_apps = self._get_rulesTree_item(self.RULES_TREE_APPS) + if rules_tree_apps != None: + self._cfg.setSettings(Config.STATS_RULES_TREE_EXPANDED_0, rules_tree_apps.isExpanded()) + rules_tree_nodes = self._get_rulesTree_item(self.RULES_TREE_NODES) + if rules_tree_nodes != None: + self._cfg.setSettings(Config.STATS_RULES_TREE_EXPANDED_1, rules_tree_nodes.isExpanded()) + + def _del_by_field(self, cur_idx, table, value): + model = self._get_active_table().model() + # get left side of the query: * GROUP BY ... + qstr = model.query().lastQuery().split("GROUP BY")[0] + # get right side of the query: ... WHERE * + q = qstr.split("WHERE") + + field = "dst_host" + if cur_idx == self.TAB_NODES: + field = "node" + elif cur_idx == self.TAB_PROCS: + field = "process" + elif cur_idx == self.TAB_ADDRS: + field = "dst_ip" + elif cur_idx == self.TAB_PORTS: + field = "dst_port" + elif cur_idx == self.TAB_USERS: + field = "uid" + + ret1 = self._db.remove("DELETE FROM {0} WHERE what = '{1}'".format(table, value)) + ret2 = self._db.remove("DELETE FROM connections WHERE {0} = '{1}'".format(field, value)) + + return ret1 and ret2 + + def _del_rule(self, rule_name, node_addr): + if rule_name == None or node_addr == None: + print("_del_rule() invalid parameters") + return + nid, noti = self._nodes.delete_rule(rule_name, node_addr, self._notification_callback) + if nid == None: + return + self._notifications_sent[nid] = noti + + # https://stackoverflow.com/questions/40225270/copy-paste-multiple-items-from-qtableview-in-pyqt4 + def _copy_selected_rows(self): + cur_idx = self.tabWidget.currentIndex() + if self.tabWidget.currentIndex() == self.TAB_RULES and self.fwTable.isVisible(): + cur_idx = self.TAB_FIREWALL + elif self.tabWidget.currentIndex() == self.TAB_RULES and not self.fwTable.isVisible(): + cur_idx = self.TAB_RULES + + selection = self.TABLES[cur_idx]['view'].copySelection() + if selection: + stream = io.StringIO() + csv.writer(stream, delimiter=',').writerows(selection) + QtWidgets.qApp.clipboard().setText(stream.getvalue()) + + def _configure_events_contextual_menu(self, pos): + try: + cur_idx = self.tabWidget.currentIndex() + table = self._get_active_table() + model = table.model() + + selection = table.selectionModel().selectedRows() + if not selection: + return False + + menu = QtWidgets.QMenu() + _menu_details = menu.addAction(QC.translate("stats", "Details")) + rulesMenu = QtWidgets.QMenu(QC.translate("stats", "Rules")) + _menu_new_rule = rulesMenu.addAction(QC.translate("stats", "New")) + menu.addMenu(rulesMenu) + + # move away menu a few pixels to the right, to avoid clicking on it by mistake + point = QtCore.QPoint(pos.x()+10, pos.y()+5) + action = menu.exec_(table.mapToGlobal(point)) + + model = table.model() + + if action == _menu_new_rule: + self._table_menu_new_rule_from_row(cur_idx, model, selection) + elif action == _menu_details: + coltime = model.index(selection[0].row(), self.COL_TIME).data() + o = ConnDetails(self) + o.showByField("time", coltime) + + except Exception as e: + print(e) + finally: + self._clear_rows_selection() + return True + + def _configure_fwrules_contextual_menu(self, pos): + try: + cur_idx = self.tabWidget.currentIndex() + table = self._get_active_table() + model = table.model() + menu = QtWidgets.QMenu() + exportMenu = QtWidgets.QMenu(QC.translate("stats", "Export")) + + selection = table.selectionModel().selectedRows() + if not selection: + return False + is_rule_enabled = model.index(selection[0].row(), FirewallTableModel.COL_ENABLED).data() + rule_action = model.index(selection[0].row(), FirewallTableModel.COL_ACTION).data() + rule_action = rule_action.lower() + + if rule_action == Config.ACTION_ACCEPT or \ + rule_action == Config.ACTION_DROP or \ + rule_action == Config.ACTION_RETURN or \ + rule_action == Config.ACTION_REJECT: + actionsMenu = QtWidgets.QMenu(QC.translate("stats", "Action")) + _action_accept = actionsMenu.addAction(Config.ACTION_ACCEPT) + _action_drop = actionsMenu.addAction(Config.ACTION_DROP) + _action_reject = actionsMenu.addAction(Config.ACTION_REJECT) + _action_return = actionsMenu.addAction(Config.ACTION_RETURN) + menu.addSeparator() + menu.addMenu(actionsMenu) + + _label_enable = QC.translate("stats", "Disable") + if is_rule_enabled == "False": + _label_enable = QC.translate("stats", "Enable") + _menu_enable = menu.addAction(_label_enable) + _menu_delete = menu.addAction(QC.translate("stats", "Delete")) + _menu_edit = menu.addAction(QC.translate("stats", "Edit")) + + menu.addSeparator() + _toClipboard = exportMenu.addAction(QC.translate("stats", "To clipboard")) + #_toDisk = exportMenu.addAction(QC.translate("stats", "To disk")) + menu.addMenu(exportMenu) + + # move away menu a few pixels to the right, to avoid clicking on it by mistake + point = QtCore.QPoint(pos.x()+10, pos.y()+5) + action = menu.exec_(table.mapToGlobal(point)) + + model = table.model() + + # block fw rules signals, to prevent reloading them per operation, + # which can lead to race conditions. + self._fw.rules.blockSignals(True) + if action == _menu_delete: + self._table_menu_delete(cur_idx, model, selection) + elif action == _menu_enable: + self._table_menu_enable(cur_idx, model, selection, is_rule_enabled) + elif action == _menu_edit: + self._table_menu_edit(cur_idx, model, selection) + elif action == _action_accept or \ + action == _action_drop or \ + action == _action_reject or \ + action == _action_return: + self._table_menu_change_rule_field(cur_idx, model, selection, FwRules.FIELD_TARGET, action.text()) + elif action == _toClipboard: + self._table_menu_export_clipboard(cur_idx, model, selection) + #elif action == _toDisk: + # self._table_menu_export_disk(cur_idx, model, selection) + + self._fw.rules.blockSignals(False) + + except Exception as e: + print("fwrules contextual menu error:", e) + finally: + self._clear_rows_selection() + return True + + def _configure_rules_contextual_menu(self, pos): + try: + cur_idx = self.tabWidget.currentIndex() + table = self._get_active_table() + model = table.model() + + selection = table.selectionModel().selectedRows() + if not selection: + return False + + menu = QtWidgets.QMenu() + durMenu = QtWidgets.QMenu(self.COL_STR_DURATION) + actionMenu = QtWidgets.QMenu(self.COL_STR_ACTION) + nodesMenu = QtWidgets.QMenu(QC.translate("stats", "Apply to")) + exportMenu = QtWidgets.QMenu(QC.translate("stats", "Export")) + + nodes_menu = [] + if self._nodes.count() > 0: + for node in self._nodes.get_nodes(): + nodes_menu.append([nodesMenu.addAction(node), node]) + menu.addMenu(nodesMenu) + + _actAllow = actionMenu.addAction(QC.translate("stats", "Allow")) + _actDeny = actionMenu.addAction(QC.translate("stats", "Deny")) + _actReject = actionMenu.addAction(QC.translate("stats", "Reject")) + menu.addMenu(actionMenu) + + _durAlways = durMenu.addAction(QC.translate("stats", "Always")) + _durUntilReboot = durMenu.addAction(QC.translate("stats", "Until reboot")) + _dur1h = durMenu.addAction(Config.DURATION_1h) + _dur30m = durMenu.addAction(Config.DURATION_30m) + _dur15m = durMenu.addAction(Config.DURATION_15m) + _dur5m = durMenu.addAction(Config.DURATION_5m) + menu.addMenu(durMenu) + + is_rule_enabled = model.index(selection[0].row(), self.COL_R_ENABLED).data() + menu_label_enable = QC.translate("stats", "Disable") + if is_rule_enabled == "False": + menu_label_enable = QC.translate("stats", "Enable") + + _menu_enable = menu.addAction(QC.translate("stats", menu_label_enable)) + _menu_duplicate = menu.addAction(QC.translate("stats", "Duplicate")) + _menu_edit = menu.addAction(QC.translate("stats", "Edit")) + _menu_delete = menu.addAction(QC.translate("stats", "Delete")) + + menu.addSeparator() + _toClipboard = exportMenu.addAction(QC.translate("stats", "To clipboard")) + _toDisk = exportMenu.addAction(QC.translate("stats", "To disk")) + menu.addMenu(exportMenu) + + # move away menu a few pixels to the right, to avoid clicking on it by mistake + point = QtCore.QPoint(pos.x()+10, pos.y()+5) + action = menu.exec_(table.mapToGlobal(point)) + + model = table.model() + + if self._nodes.count() > 0: + for nmenu in nodes_menu: + node_action = nmenu[0] + node_addr = nmenu[1] + if action == node_action: + ret = Message.yes_no( + QC.translate("stats", " Apply this rule to {0} ".format(node_addr)), + QC.translate("stats", " Are you sure?"), + QtWidgets.QMessageBox.Warning) + if ret == QtWidgets.QMessageBox.Cancel: + return False + self._table_menu_apply_to_node(cur_idx, model, selection, node_addr) + return False + + if action == _menu_delete: + self._table_menu_delete(cur_idx, model, selection) + elif action == _menu_edit: + self._table_menu_edit(cur_idx, model, selection) + elif action == _menu_enable: + self._table_menu_enable(cur_idx, model, selection, is_rule_enabled) + elif action == _menu_duplicate: + self._table_menu_duplicate(cur_idx, model, selection) + elif action == _durAlways: + self._table_menu_change_rule_field(cur_idx, model, selection, "duration", Config.DURATION_ALWAYS) + elif action == _dur1h: + self._table_menu_change_rule_field(cur_idx, model, selection, "duration", Config.DURATION_1h) + elif action == _dur30m: + self._table_menu_change_rule_field(cur_idx, model, selection, "duration", Config.DURATION_30m) + elif action == _dur15m: + self._table_menu_change_rule_field(cur_idx, model, selection, "duration", Config.DURATION_15m) + elif action == _dur5m: + self._table_menu_change_rule_field(cur_idx, model, selection, "duration", Config.DURATION_5m) + elif action == _durUntilReboot: + self._table_menu_change_rule_field(cur_idx, model, selection, "duration", Config.DURATION_UNTIL_RESTART) + elif action == _actAllow: + self._table_menu_change_rule_field(cur_idx, model, selection, "action", Config.ACTION_ALLOW) + elif action == _actDeny: + self._table_menu_change_rule_field(cur_idx, model, selection, "action", Config.ACTION_DENY) + elif action == _actReject: + self._table_menu_change_rule_field(cur_idx, model, selection, "action", Config.ACTION_REJECT) + elif action == _toClipboard: + self._table_menu_export_clipboard(cur_idx, model, selection) + elif action == _toDisk: + self._table_menu_export_disk(cur_idx, model, selection) + + except Exception as e: + print("rules contextual menu exception:", e) + finally: + self._clear_rows_selection() + return True + + def _table_menu_export_clipboard(self, cur_idx, model, selection): + rules_list = [] + if cur_idx == self.TAB_RULES and not self.fwTable.isVisible(): + for idx in selection: + rule_name = model.index(idx.row(), self.COL_R_NAME).data() + node_addr = model.index(idx.row(), self.COL_R_NODE).data() + + json_rule = self._nodes.rule_to_json(node_addr, rule_name) + if json_rule != None: + rules_list.append(json_rule) + else: + print("export to clipboard: ERROR converting \"{0}\" to json".format(rule_name)) + elif cur_idx == self.TAB_RULES and self.fwTable.isVisible(): + for idx in selection: + uuid = model.index(idx.row(), FirewallTableModel.COL_UUID).data() + node = model.index(idx.row(), FirewallTableModel.COL_ADDR).data() + r = self._fw.get_protorule_by_uuid(node, uuid) + if r: + rules_list.append(self._fw.rule_to_json(r)) + + cliptext="" + for r in rules_list: + cliptext = "{0}\n{1}".format(cliptext, r) + + QtWidgets.qApp.clipboard().setText(cliptext) + + def _table_menu_export_disk(self, cur_idx, model, selection): + outdir = QtWidgets.QFileDialog.getExistingDirectory(self, + os.path.expanduser("~"), + QC.translate("stats", 'Select a directory to export rules'), + QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontResolveSymlinks) + if outdir == "": + return + + error_list = [] + for idx in selection: + rule_name = model.index(idx.row(), self.COL_R_NAME).data() + node_addr = model.index(idx.row(), self.COL_R_NODE).data() + + ok = self._nodes.export_rule(node_addr, rule_name, outdir) + if not ok: + error_list.append(rule_name) + + if len(error_list) == 0: + Message.ok("Rules export", + QC.translate("stats", "Rules exported to {0}".format(outdir)), + QtWidgets.QMessageBox.Information) + else: + error_text = "" + for e in error_list: + error_text = "{0}<br>{1}".format(error_text, e) + + Message.ok("Rules export error", + QC.translate("stats", + "Error exporting the following rules:<br><br>".format(error_text) + ), + QtWidgets.QMessageBox.Warning) + + def _table_menu_duplicate(self, cur_idx, model, selection): + + for idx in selection: + rule_name = model.index(idx.row(), self.COL_R_NAME).data() + node_addr = model.index(idx.row(), self.COL_R_NODE).data() + + records = None + for idx in range(0,100): + records = self._get_rule(rule_name, node_addr) + if records == None or records.size() == -1: + rule = Rule.new_from_records(records) + rule.name = "cloned-{0}-{1}".format(idx, rule.name) + self._rules.add_rules(node_addr, [rule]) + break + + if records != None and records.size() == -1: + noti = ui_pb2.Notification(type=ui_pb2.CHANGE_RULE, rules=[rule]) + nid = self._nodes.send_notification(node_addr, noti, self._notification_callback) + if nid != None: + self._notifications_sent[nid] = noti + + def _table_menu_apply_to_node(self, cur_idx, model, selection, node_addr): + + for idx in selection: + rule_name = model.index(idx.row(), self.COL_R_NAME).data() + records = self._get_rule(rule_name, None) + rule = Rule.new_from_records(records) + + noti = ui_pb2.Notification(type=ui_pb2.CHANGE_RULE, rules=[rule]) + nid = self._nodes.send_notification(node_addr, noti, self._notification_callback) + if nid != None: + self._rules.add_rules(node_addr, [rule]) + self._notifications_sent[nid] = noti + + def _table_menu_change_rule_field(self, cur_idx, model, selection, field, value): + if cur_idx == self.TAB_RULES and not self.fwTable.isVisible(): + for idx in selection: + rule_name = model.index(idx.row(), self.COL_R_NAME).data() + node_addr = model.index(idx.row(), self.COL_R_NODE).data() + + records = self._get_rule(rule_name, node_addr) + rule = Rule.new_from_records(records) + + self._db.update(table="rules", fields="{0}=?".format(field), + values=[value], condition="name='{0}' AND node='{1}'".format(rule_name, node_addr), + action_on_conflict="") + + if field == "action": + rule.action = value + elif field == "duration": + rule.duration = value + elif field == "precedence": + rule.precedence = value + + noti = ui_pb2.Notification(type=ui_pb2.CHANGE_RULE, rules=[rule]) + nid = self._nodes.send_notification(node_addr, noti, self._notification_callback) + if nid != None: + self._notifications_sent[nid] = noti + elif cur_idx == self.TAB_RULES and self.fwTable.isVisible(): + nodes_updated = [] + for idx in selection: + uuid = model.index(idx.row(), FirewallTableModel.COL_UUID).data() + node = model.index(idx.row(), FirewallTableModel.COL_ADDR).data() + updated, err = self._fw.change_rule_field(node, uuid, field, value) + if updated: + nodes_updated.append(node) + else: + print("error updating fw rule field", field, "value:", value) + + for addr in nodes_updated: + node = self._nodes.get_node(addr) + nid, noti = self._nodes.reload_fw(addr, node['firewall'], self._notification_callback) + self._notifications_sent[nid] = noti + + def _table_menu_enable(self, cur_idx, model, selection, is_rule_enabled): + rule_status = "False" if is_rule_enabled == "True" else "True" + enable_rule = False if is_rule_enabled == "True" else True + + if cur_idx == self.TAB_RULES and not self.fwTable.isVisible(): + for idx in selection: + rule_name = model.index(idx.row(), self.COL_R_NAME).data() + node_addr = model.index(idx.row(), self.COL_R_NODE).data() + + records = self._get_rule(rule_name, node_addr) + rule = Rule.new_from_records(records) + rule_type = ui_pb2.DISABLE_RULE if is_rule_enabled == "True" else ui_pb2.ENABLE_RULE + + self._db.update(table="rules", fields="enabled=?", + values=[rule_status], condition="name='{0}' AND node='{1}'".format(rule_name, node_addr), + action_on_conflict="") + + noti = ui_pb2.Notification(type=rule_type, rules=[rule]) + nid = self._nodes.send_notification(node_addr, noti, self._notification_callback) + if nid != None: + self._notifications_sent[nid] = noti + + elif cur_idx == self.TAB_RULES and self.fwTable.isVisible(): + nodes_updated = [] + for idx in selection: + uuid = model.index(idx.row(), FirewallTableModel.COL_UUID).data() + node = model.index(idx.row(), FirewallTableModel.COL_ADDR).data() + updated, err = self._fw.enable_rule(node, uuid, enable_rule) + if updated: + nodes_updated.append(node) + + for addr in nodes_updated: + node = self._nodes.get_node(addr) + nid, noti = self._nodes.reload_fw(addr, node['firewall'], self._notification_callback) + self._notifications_sent[nid] = noti + + def _table_menu_delete(self, cur_idx, model, selection): + if cur_idx == self.TAB_MAIN or cur_idx == self.TAB_NODES or self.IN_DETAIL_VIEW[cur_idx]: + return + + msg = QC.translate("stats", " You are about to delete this rule. ") + if cur_idx != self.TAB_RULES: + msg = QC.translate("stats", " You are about to delete this entry. ") + + ret = Message.yes_no(msg, + QC.translate("stats", " Are you sure?"), + QtWidgets.QMessageBox.Warning) + if ret == QtWidgets.QMessageBox.Cancel: + return False + + if cur_idx == self.TAB_RULES and self.fwTable.isVisible(): + nodes_updated = {} + for idx in selection: + uuid = model.index(idx.row(), FirewallTableModel.COL_UUID).data() + node = model.index(idx.row(), FirewallTableModel.COL_ADDR).data() + ok, fw_config = self._fw.delete_rule(node, uuid) + if ok: + nodes_updated[node] = fw_config + else: + print("error deleting fw rule:", uuid, "row:", idx.row()) + + for addr in nodes_updated: + nid, noti = self._nodes.reload_fw(addr, nodes_updated[addr], self._notification_callback) + self._notifications_sent[nid] = noti + + elif cur_idx == self.TAB_RULES and not self.fwTable.isVisible(): + for idx in selection: + name = model.index(idx.row(), self.COL_R_NAME).data() + node = model.index(idx.row(), self.COL_R_NODE).data() + self._del_rule(name, node) + self._refresh_active_table() + + elif cur_idx == self.TAB_HOSTS or cur_idx == self.TAB_PROCS or cur_idx == self.TAB_ADDRS or \ + cur_idx == self.TAB_USERS or cur_idx == self.TAB_PORTS: + do_refresh = False + for idx in selection: + field = model.index(idx.row(), self.COL_WHAT).data() + if field == "": + continue + ok = self._del_by_field(cur_idx, self.TABLES[cur_idx]['name'], field) + do_refresh |= ok + if do_refresh: + self._refresh_active_table() + + def _table_menu_new_rule_from_row(self, cur_idx, model, selection): + coltime = model.index(selection[0].row(), self.COL_TIME).data() + if self._rules_dialog.new_rule_from_connection(coltime) == False: + + Message.ok(QC.translate("stats", "New rule error"), + QC.translate("stats", + "Error creating new rule from event ({0})".format(coltime) + ), + QtWidgets.QMessageBox.Warning) + + def _table_menu_edit(self, cur_idx, model, selection): + if cur_idx == self.TAB_RULES and not self.fwTable.isVisible(): + for idx in selection: + name = model.index(idx.row(), self.COL_R_NAME).data() + node = model.index(idx.row(), self.COL_R_NODE).data() + records = self._get_rule(name, node) + if records == None or records == -1: + Message.ok(QC.translate("stats", "New rule error"), + QC.translate("stats", "Rule not found by that name and node"), + QtWidgets.QMessageBox.Warning) + return + self._rules_dialog.edit_rule(records, node) + break + + elif cur_idx == self.TAB_RULES and self.fwTable.isVisible(): + for idx in selection: + uuid = model.index(idx.row(), FirewallTableModel.COL_UUID).data() + node = model.index(idx.row(), FirewallTableModel.COL_ADDR).data() + self._fw_dialog.load_rule(node, uuid) + + break + + def _cb_fw_rules_updated(self): + self._add_rulesTree_fw_chains() + + def _cb_app_rules_updated(self, what): + self._refresh_active_table() + + @QtCore.pyqtSlot(str) + def _cb_fw_table_rows_reordered(self, node_addr): + node = self._nodes.get_node(node_addr) + nid, notif = self._nodes.reload_fw(node_addr, node['firewall'], self._notification_callback) + self._notifications_sent[nid] = {'addr': node_addr, 'notif': notif} + + # ignore updates while the user is using the scrollbar. + def _cb_scrollbar_pressed(self): + self.scrollbar_active = True + + def _cb_scrollbar_released(self): + self.scrollbar_active = False + + def _cb_tree_edit_firewall_clicked(self): + self._fw_dialog.show() + + def _cb_proc_details_clicked(self): + table = self._tables[self.tabWidget.currentIndex()] + nrows = table.model().rowCount() + pids = {} + for row in range(0, nrows): + pid = table.model().index(row, self.COL_PROC_PID).data() + node = table.model().index(row, self.COL_NODE).data() + if pid not in pids: + pids[pid] = node + + self._proc_details_dialog.monitor(pids) + + @QtCore.pyqtSlot(ui_pb2.NotificationReply) + def _cb_notification_callback(self, reply): + if reply.id in self._notifications_sent: + if reply.code == ui_pb2.ERROR: + Message.ok( + QC.translate("stats", "Error:"), + "{0}".format(reply.data), + QtWidgets.QMessageBox.Warning) + + del self._notifications_sent[reply.id] + + else: + Message.ok( + QC.translate("stats", "Warning:"), + "{0}".format(reply.data), + QtWidgets.QMessageBox.Warning) + + def _cb_tab_changed(self, index): + self.comboAction.setVisible(index == self.TAB_MAIN) + + self.TABLES[index]['cmdCleanStats'].setVisible(True) + if index == self.TAB_MAIN: + self._set_events_query() + else: + if index == self.TAB_RULES: + # display the clean buton only if not in detail view + self.TABLES[index]['cmdCleanStats'].setVisible( self.IN_DETAIL_VIEW[index] ) + self._add_rulesTree_nodes() + + elif index == self.TAB_PROCS: + # make the button visible depending if we're in the detail view + nrows = self._get_active_table().model().rowCount() + self.cmdProcDetails.setVisible(self.IN_DETAIL_VIEW[index] and nrows > 0) + elif index == self.TAB_NODES: + self.TABLES[index]['cmdCleanStats'].setVisible( self.IN_DETAIL_VIEW[index] ) + + self._refresh_active_table() + + def _cb_table_context_menu(self, pos): + cur_idx = self.tabWidget.currentIndex() + if cur_idx != self.TAB_RULES and cur_idx != self.TAB_MAIN: + # the only tables with context menu for now are events and rules table + return + if self.IN_DETAIL_VIEW[self.TAB_RULES] == True: + return + + self._context_menu_active = True + if cur_idx == self.TAB_MAIN: + refresh_table = self._configure_events_contextual_menu(pos) + elif cur_idx == self.TAB_RULES: + if self.fwTable.isVisible(): + refresh_table = self._configure_fwrules_contextual_menu(pos) + else: + refresh_table = self._configure_rules_contextual_menu(pos) + + self._context_menu_active = False + if refresh_table: + self._refresh_active_table() + + + def _cb_table_header_clicked(self, pos, sortIdx): + cur_idx = self.tabWidget.currentIndex() + # TODO: allow ordering by Network column + if cur_idx == self.TAB_ADDRS and pos == 2: + return + + model = self._get_active_table().model() + qstr = model.query().lastQuery().split("ORDER BY")[0] + + q = qstr.strip(" ") + " ORDER BY %d %s" % (pos+1, self.SORT_ORDER[sortIdx]) + if cur_idx > 0 and self.TABLES[cur_idx]['cmd'].isVisible() == False: + self.TABLES[cur_idx]['last_order_by'] = pos+1 + self.TABLES[cur_idx]['last_order_to'] = sortIdx + + q = qstr.strip(" ") + self._get_order() + + q += self._get_limit() + self.setQuery(model, q) + + def _cb_events_filter_line_changed(self, text): + cur_idx = self.tabWidget.currentIndex() + + model = self.TABLES[cur_idx]['view'].model() + qstr = None + if cur_idx == StatsDialog.TAB_MAIN: + self._cfg.setSettings(Config.STATS_FILTER_TEXT, text) + self._set_events_query() + return + elif cur_idx == StatsDialog.TAB_NODES: + qstr = self._get_nodes_filter_query(model.query().lastQuery(), text) + elif cur_idx == StatsDialog.TAB_RULES and self.fwTable.isVisible(): + self.TABLES[self.TAB_FIREWALL]['view'].filterByQuery(text) + return + elif self.IN_DETAIL_VIEW[cur_idx] == True: + qstr = self._get_indetail_filter_query(model.query().lastQuery(), text) + else: + where_clause = self._get_filter_line_clause(cur_idx, text) + qstr = self._db.get_query( self.TABLES[cur_idx]['name'], self.TABLES[cur_idx]['display_fields'] ) + \ + where_clause + self._get_order() + if text == "": + qstr = qstr + self._get_limit() + + if qstr != None: + self.setQuery(model, qstr) + + def _cb_limit_combo_changed(self, idx): + if self.tabWidget.currentIndex() == self.TAB_MAIN: + self._set_events_query() + else: + model = self._get_active_table().model() + qstr = model.query().lastQuery() + if "LIMIT" in qstr: + qs = qstr.split(" LIMIT ") + q = qs[0] + l = qs[1] + qstr = q + self._get_limit() + else: + qstr = qstr + self._get_limit() + self.setQuery(model, qstr) + + def _cb_combo_action_changed(self, idx): + if self.tabWidget.currentIndex() != self.TAB_MAIN: + return + + self._cfg.setSettings(Config.STATS_GENERAL_FILTER_ACTION, idx) + self._set_events_query() + + def _cb_clean_sql_clicked(self, idx): + cur_idx = self.tabWidget.currentIndex() + if self.tabWidget.currentIndex() == StatsDialog.TAB_RULES: + self._db.empty_rule(self.TABLES[cur_idx]['label'].text()) + elif self.IN_DETAIL_VIEW[cur_idx]: + self._del_by_field(cur_idx, self.TABLES[cur_idx]['name'], self.TABLES[cur_idx]['label'].text()) + else: + self._db.clean(self.TABLES[cur_idx]['name']) + self._refresh_active_table() + + def _cb_cmd_back_clicked(self, idx): + try: + cur_idx = self.tabWidget.currentIndex() + self.IN_DETAIL_VIEW[cur_idx] = False + + self._set_active_widgets(cur_idx, False) + if cur_idx == StatsDialog.TAB_RULES: + self._restore_rules_tab_widgets(True) + return + elif cur_idx == StatsDialog.TAB_PROCS: + self.cmdProcDetails.setVisible(False) + + model = self._get_active_table().model() + where_clause = "" + if self.TABLES[cur_idx]['filterLine'] != None: + filter_text = self.TABLES[cur_idx]['filterLine'].text() + where_clause = self._get_filter_line_clause(cur_idx, filter_text) + + self.setQuery(model, + self._db.get_query( + self.TABLES[cur_idx]['name'], + self.TABLES[cur_idx]['display_fields']) + where_clause + " " + self._get_order() + self._get_limit() + ) + finally: + self._restore_details_view_columns( + self.TABLES[cur_idx]['view'].horizontalHeader(), + "{0}{1}".format(Config.STATS_VIEW_COL_STATE, cur_idx) + ) + self._restore_scroll_value() + self._restore_last_selected_row() + + def _cb_main_table_double_clicked(self, row): + prev_idx = self.tabWidget.currentIndex() + data = row.data() + idx = row.column() + cur_idx = 1 + + if idx == StatsDialog.COL_NODE: + cur_idx = self.TAB_NODES + self.IN_DETAIL_VIEW[cur_idx] = True + self.LAST_SELECTED_ITEM = row.model().index(row.row(), self.COL_NODE).data() + self.tabWidget.setCurrentIndex(cur_idx) + self._set_active_widgets(prev_idx, True, str(data)) + self._set_nodes_query(data) + + elif idx == StatsDialog.COL_RULES: + cur_idx = self.TAB_RULES + self.IN_DETAIL_VIEW[cur_idx] = True + self.LAST_SELECTED_ITEM = row.model().index(row.row(), self.COL_RULES).data() + r_name, node = self._set_rules_tab_active(row, cur_idx, self.COL_RULES, self.COL_NODE) + self._set_active_widgets(prev_idx, True, str(data)) + self._set_rules_query(r_name, node) + + elif idx == StatsDialog.COL_DSTIP: + cur_idx = self.TAB_ADDRS + self.IN_DETAIL_VIEW[cur_idx] = True + rowdata = row.model().index(row.row(), self.COL_DSTIP).data() + ip = rowdata + self.LAST_SELECTED_ITEM = ip + self.tabWidget.setCurrentIndex(cur_idx) + self._set_active_widgets(prev_idx, True, ip) + self._set_addrs_query(ip) + + elif idx == StatsDialog.COL_DSTHOST: + cur_idx = self.TAB_HOSTS + self.IN_DETAIL_VIEW[cur_idx] = True + rowdata = row.model().index(row.row(), self.COL_DSTHOST).data() + host = rowdata + self.LAST_SELECTED_ITEM = host + self.tabWidget.setCurrentIndex(cur_idx) + self._set_active_widgets(prev_idx, True, host) + self._set_hosts_query(host) + + elif idx == StatsDialog.COL_DSTPORT: + cur_idx = self.TAB_PORTS + self.IN_DETAIL_VIEW[cur_idx] = True + rowdata = row.model().index(row.row(), self.COL_DSTPORT).data() + port = rowdata + self.LAST_SELECTED_ITEM = port + self.tabWidget.setCurrentIndex(cur_idx) + self._set_active_widgets(prev_idx, True, port) + self._set_ports_query(port) + + elif idx == StatsDialog.COL_UID: + cur_idx = self.TAB_USERS + self.IN_DETAIL_VIEW[cur_idx] = True + rowdata = row.model().index(row.row(), self.COL_UID).data() + uid = rowdata + self.LAST_SELECTED_ITEM = uid + self.tabWidget.setCurrentIndex(cur_idx) + self._set_active_widgets(prev_idx, True, uid) + self._set_users_query(uid) + + else: + cur_idx = self.TAB_PROCS + self.IN_DETAIL_VIEW[cur_idx] = True + self.LAST_SELECTED_ITEM = row.model().index(row.row(), self.COL_PROCS).data() + self.tabWidget.setCurrentIndex(cur_idx) + self._set_active_widgets(prev_idx, True, self.LAST_SELECTED_ITEM) + self._set_process_query(self.LAST_SELECTED_ITEM) + + self._restore_details_view_columns( + self.TABLES[cur_idx]['view'].horizontalHeader(), + "{0}{1}".format(Config.STATS_VIEW_DETAILS_COL_STATE, cur_idx) + ) + + def _cb_table_double_clicked(self, row): + cur_idx = self.tabWidget.currentIndex() + if self.IN_DETAIL_VIEW[cur_idx]: + return + + if cur_idx == self.TAB_RULES and self.fwTable.isVisible(): + uuid = row.model().index(row.row(), 1).data(QtCore.Qt.UserRole+1) + addr = row.model().index(row.row(), 2).data(QtCore.Qt.UserRole+1) + self._fw_dialog.load_rule(addr, uuid) + return + + + self.IN_DETAIL_VIEW[cur_idx] = True + self.LAST_SELECTED_ITEM = row.model().index(row.row(), self.COL_TIME).data() + self.LAST_SCROLL_VALUE = self.TABLES[cur_idx]['view'].vScrollBar.value() + + data = row.data() + + if cur_idx == self.TAB_RULES: + rule_name = row.model().index(row.row(), self.COL_R_NAME).data() + self._set_active_widgets(cur_idx, True, rule_name) + r_name, node = self._set_rules_tab_active(row, cur_idx, self.COL_R_NAME, self.COL_R_NODE) + self.LAST_SELECTED_ITEM = row.model().index(row.row(), self.COL_R_NAME).data() + self._set_rules_query(r_name, node) + self._restore_details_view_columns( + self.TABLES[cur_idx]['view'].horizontalHeader(), + "{0}{1}".format(Config.STATS_VIEW_DETAILS_COL_STATE, cur_idx) + ) + return + if cur_idx == self.TAB_NODES: + data = row.model().index(row.row(), self.COL_NODE).data() + self.LAST_SELECTED_ITEM = row.model().index(row.row(), self.COL_NODE).data() + if cur_idx > self.TAB_RULES: + self.LAST_SELECTED_ITEM = row.model().index(row.row(), self.COL_WHAT).data() + data = row.model().index(row.row(), self.COL_WHAT).data() + + + self._set_active_widgets(cur_idx, True, str(data)) + + if cur_idx == StatsDialog.TAB_NODES: + self._set_nodes_query(data) + elif cur_idx == StatsDialog.TAB_HOSTS: + self._set_hosts_query(data) + elif cur_idx == StatsDialog.TAB_PROCS: + self._set_process_query(data) + elif cur_idx == StatsDialog.TAB_ADDRS: + lbl_text = self.TABLES[cur_idx]['label'].text() + if lbl_text != "": + asn = self.asndb.get_asn(lbl_text) + if asn != "": + lbl_text += " (" + asn + ")" + self.TABLES[cur_idx]['label'].setText(lbl_text) + self._set_addrs_query(data) + elif cur_idx == StatsDialog.TAB_PORTS: + self._set_ports_query(data) + elif cur_idx == StatsDialog.TAB_USERS: + self._set_users_query(data) + + self._restore_details_view_columns( + self.TABLES[cur_idx]['view'].horizontalHeader(), + "{0}{1}".format(Config.STATS_VIEW_DETAILS_COL_STATE, cur_idx) + ) + + def _cb_prefs_clicked(self): + self._prefs_dialog.show() + + def _cb_rules_filter_combo_changed(self, idx): + if idx == self.RULES_TREE_APPS: + self._set_rules_filter() + elif idx == self.RULES_COMBO_PERMANENT: + self._set_rules_filter(self.RULES_TREE_APPS, self.RULES_TREE_PERMANENT) + elif idx == self.RULES_COMBO_TEMPORARY: + self._set_rules_filter(self.RULES_TREE_APPS, self.RULES_TREE_TEMPORARY) + elif idx == self.RULES_COMBO_FW: + self._set_rules_filter(-1, self.RULES_TREE_FIREWALL) + + def _cb_rules_tree_item_expanded(self, item): + self.rulesTreePanel.resizeColumnToContents(0) + self.rulesTreePanel.resizeColumnToContents(1) + + def _cb_rules_tree_item_double_clicked(self, item, col): + # TODO: open fw chain editor + pass + + def _cb_rules_tree_item_clicked(self, item, col): + """ + Event fired when the user clicks on the left panel of the rules tab + """ + item_model = self.rulesTreePanel.indexFromItem(item, col) + item_row = item_model.row() + parent = item.parent() + parent_row = -1 + node_addr = "" + fw_table = "" + + rulesHeader = self.rulesTable.horizontalHeader() + self._cfg.setSettings(Config.STATS_RULES_COL_STATE, rulesHeader.saveState()) + + self._clear_rows_selection() + + # FIXME: find a clever way of handling these options + + # top level items + if parent != None: + parent_model = self.rulesTreePanel.indexFromItem(parent, 0) + parent_row = parent_model.row() + node_addr = parent_model.data() + + # 1st level items: nodes, rules types + if parent.parent() != None: + parent = parent.parent() + parent_model = self.rulesTreePanel.indexFromItem(parent, 0) + item_row = self.FILTER_TREE_FW_TABLE + parent_row = self.RULES_TREE_FIREWALL + fw_table = parent_model.data() + + # 2nd level items: chains + if parent.parent() != None: + parent = parent.parent() + parent_model = self.rulesTreePanel.indexFromItem(parent.parent(), 0) + item_row = self.FILTER_TREE_FW_CHAIN + parent_row = self.RULES_TREE_FIREWALL + + if node_addr == None: + return + + showFwTable = (parent_row == self.RULES_TREE_FIREWALL or (parent_row == -1 and item_row == self.RULES_TREE_FIREWALL)) + self.fwTable.setVisible(showFwTable) + self.rulesTable.setVisible(not showFwTable) + self.rulesScrollBar.setVisible(self.rulesTable.isVisible()) + + self._set_rules_filter(parent_row, item_row, item.text(0), node_addr, fw_table) + + def _cb_rules_splitter_moved(self, pos, index): + self.comboRulesFilter.setVisible(pos == 0) + self._cfg.setSettings(Config.STATS_RULES_SPLITTER_POS, self.rulesSplitter.saveState()) + + def _cb_start_clicked(self): + if self.daemon_connected == False: + self.startButton.setChecked(False) + self.startButton.setIcon(self.iconStart) + return + + self.update_interception_status(self.startButton.isChecked()) + self._status_changed_trigger.emit(self.startButton.isChecked()) + + if self.startButton.isChecked(): + nid, noti = self._nodes.start_interception(_callback=self._notification_callback) + else: + nid, noti = self._nodes.stop_interception(_callback=self._notification_callback) + + self._notifications_sent[nid] = noti + + def _cb_node_start_clicked(self): + addr = self.TABLES[self.TAB_NODES]['label'].text() + if addr == "": + return + if self.nodeStartButton.isChecked(): + self._update_nodes_interception_status() + nid, noti = self._nodes.start_interception(_addr=addr, _callback=self._notification_callback) + else: + self._update_nodes_interception_status(disable=True) + nid, noti = self._nodes.stop_interception(_addr=addr, _callback=self._notification_callback) + + self._notifications_sent[nid] = noti + + def _cb_node_prefs_clicked(self): + addr = self.TABLES[self.TAB_NODES]['label'].text() + if addr == "": + return + self._prefs_dialog.show_node_prefs(addr) + + def _cb_node_delete_clicked(self): + ret = Message.yes_no( + QC.translate("stats", " You are about to delete this node. "), + QC.translate("stats", " Are you sure?"), + QtWidgets.QMessageBox.Warning) + if ret == QtWidgets.QMessageBox.Cancel: + return + + addr = self.TABLES[self.TAB_NODES]['label'].text() + if self._db.remove("DELETE FROM nodes WHERE addr = '{0}'".format(addr)) == False: + Message.ok( + QC.translate("stats", + "<b>Error deleting node</b><br><br>", + "{0}").format(addr), + QtWidgets.QMessageBox.Warning) + return + + self._nodes.delete(addr) + self.TABLES[self.TAB_NODES]['cmd'].click() + self.TABLES[self.TAB_NODES]['label'].setText("") + self._refresh_active_table() + + def _cb_new_rule_clicked(self): + self._rules_dialog.new_rule() + + def _cb_edit_rule_clicked(self): + cur_idx = self.tabWidget.currentIndex() + records = self._get_rule(self.TABLES[cur_idx]['label'].text(), self.nodeRuleLabel.text()) + if records == None: + return + + self._rules_dialog.edit_rule(records, self.nodeRuleLabel.text()) + + def _cb_del_rule_clicked(self): + ret = Message.yes_no( + QC.translate("stats", " You are about to delete this rule. "), + QC.translate("stats", " Are you sure?"), + QtWidgets.QMessageBox.Warning) + if ret == QtWidgets.QMessageBox.Cancel: + return + + self._del_rule(self.TABLES[self.tabWidget.currentIndex()]['label'].text(), self.nodeRuleLabel.text()) + self.TABLES[self.TAB_RULES]['cmd'].click() + self.nodeRuleLabel.setText("") + self._refresh_active_table() + + def _cb_enable_rule_toggled(self, state): + rule = ui_pb2.Rule(name=self.TABLES[self.tabWidget.currentIndex()]['label'].text()) + rule.enabled = False + rule.action = "" + rule.duration = "" + rule.operator.type = "" + rule.operator.operand = "" + rule.operator.data = "" + + notType = ui_pb2.DISABLE_RULE + if state == True: + notType = ui_pb2.ENABLE_RULE + rule.enabled = state + noti = ui_pb2.Notification(type=notType, rules=[rule]) + self._notification_trigger.emit(noti) + + def _cb_prev_button_clicked(self): + model = self._get_active_table().model() + model.fetchMore() + + def _cb_next_button_clicked(self): + model = self._get_active_table().model() + model.fetchMore() + + def _cb_help_button_clicked(self): + QuickHelp.show( + QC.translate("stats", + "<p><b>Quick help</b></p>" \ + "<p>- Use CTRL+c to copy selected rows.</p>" \ + "<p>- Use Home,End,PgUp,PgDown,PgUp,Up or Down keys to navigate rows.</p>" \ + "<p>- Use right click on a row to stop refreshing the view.</p>" \ + "<p>- Selecting more than one row also stops refreshing the view.</p>" + "<p>- On the Events view, clicking on columns Node, Process or Rule<br>" \ + "jumps to the view of the selected item.</p>" \ + "<p>- On the rest of the views, double click on a row to get detailed<br>" \ + " information.</p><br>" \ + "<p>For more information visit the <a href=\"{0}\">wiki</a></p>" \ + "<br>".format(Config.HELP_URL) + ) + ) + + # must be called after setModel() or setQuery() + def _show_columns(self): + cols = self._cfg.getSettings(Config.STATS_SHOW_COLUMNS) + if cols == None: + return + + for c in range(StatsDialog.GENERAL_COL_NUM): + self.eventsTable.setColumnHidden(c, str(c) not in cols) + + def _update_status_label(self, running=False, text=FIREWALL_DISABLED): + self.statusLabel.setText("%12s" % text) + if running: + self.statusLabel.setStyleSheet('color: green; margin: 5px') + self.startButton.setIcon(self.iconPause) + else: + self.statusLabel.setStyleSheet('color: rgb(206, 92, 0); margin: 5px') + self.startButton.setIcon(self.iconStart) + + self._add_rulesTree_nodes() + self._add_rulesTree_fw_chains() + + def _get_rulesTree_item(self, index): + try: + return self.rulesTreePanel.topLevelItem(index) + except Exception: + return None + + def _add_rulesTree_nodes(self): + if self._nodes.count() > 0: + nodesItem = self.rulesTreePanel.topLevelItem(self.RULES_TREE_NODES) + nodesItem.takeChildren() + for n in self._nodes.get_nodes(): + nodesItem.addChild(QtWidgets.QTreeWidgetItem([n])) + + def _find_tree_fw_items(self, item_data): + """find fw items by data stored in UserRole role. + """ + fwItem = self.rulesTreePanel.topLevelItem(self.RULES_TREE_FIREWALL) + it = QtWidgets.QTreeWidgetItemIterator(fwItem) + items = [] + while it.value(): + x = it.value() + if x.data(0, QtCore.Qt.UserRole) == item_data: + items.append(x) + it+=1 + + return items + + def _add_rulesTree_fw_chains(self): + expanded = list() + selected = None + scrollValue = self.rulesTreePanel.verticalScrollBar().value() + fwItem = self.rulesTreePanel.topLevelItem(self.RULES_TREE_FIREWALL) + it = QtWidgets.QTreeWidgetItemIterator(fwItem) + # save tree selected rows + try: + while it.value(): + x = it.value() + if x.isExpanded(): + expanded.append(x) + if x.isSelected(): + selected = x + it += 1 + except Exception: + pass + + self.rulesTreePanel.setAnimated(False) + fwItem.takeChildren() + self.rulesTreePanel.setItemWidget(fwItem, 1, self.fwTreeEdit) + chains = self._fw.get_chains() + for addr in chains: + # add nodes + nodeRoot = QtWidgets.QTreeWidgetItem(["{0}".format(addr)]) + nodeRoot.setData(0, QtCore.Qt.UserRole, addr) + fwItem.addChild(nodeRoot) + for nodeChains in chains[addr]: + # exclude legacy system rules + if len(nodeChains) == 0: + continue + for cc in nodeChains: + # add tables + tableName = "{0}-{1}".format(cc.Table, cc.Family) + nodeTable = QtWidgets.QTreeWidgetItem([tableName]) + nodeTable.setData(0, QtCore.Qt.UserRole, "{0}-{1}".format(addr, tableName)) + + chainName = "{0}-{1}".format(cc.Name, cc.Hook) + nodeChain = QtWidgets.QTreeWidgetItem([chainName, cc.Policy]) + nodeChain.setData(0, QtCore.Qt.UserRole, "{0}-{1}".format(addr, chainName)) + + items = self._find_tree_fw_items("{0}-{1}".format(addr, tableName)) + if len(items) == 0: + # add table + nodeTable.addChild(nodeChain) + nodeRoot.addChild(nodeTable) + else: + # add chains + node = items[0] + node.addChild(nodeChain) + + # restore previous selected rows + try: + for item in expanded: + items = self.rulesTreePanel.findItems(item.text(0), QtCore.Qt.MatchRecursive) + for it in items: + it.setExpanded(True) + if selected != None and selected.text(0) == it.text(0): + it.setSelected(True) + except: + pass + + self.rulesTreePanel.verticalScrollBar().setValue(scrollValue) + self.rulesTreePanel.setAnimated(True) + self.rulesTreePanel.resizeColumnToContents(0) + self.rulesTreePanel.resizeColumnToContents(1) + expanded = None + + def _clear_rows_selection(self): + cur_idx = self.tabWidget.currentIndex() + self.TABLES[cur_idx]['view'].clearSelection() + + def _are_rows_selected(self): + cur_idx = self.tabWidget.currentIndex() + view = self.TABLES[cur_idx]['view'] + ret = False + if view != None: + ret = len(view.selectionModel().selectedRows(0)) > 0 + return ret + + def _get_rule(self, rule_name, node_name): + """ + get rule records, given the name of the rule and the node + """ + cur_idx = self.tabWidget.currentIndex() + records = self._db.get_rule(rule_name, node_name) + if records.next() == False: + print("[stats dialog] edit rule, no records: ", rule_name, node_name) + if self.TABLES[cur_idx]['cmd'] != None: + self.TABLES[cur_idx]['cmd'].click() + return None + + return records + + def _get_filter_line_clause(self, idx, text): + if text == "": + return "" + + if idx == StatsDialog.TAB_RULES: + return " WHERE rules.name LIKE '%{0}%' OR rules.operator_data LIKE '%{1}%' ".format(text, text) + elif idx == StatsDialog.TAB_HOSTS or \ + idx == StatsDialog.TAB_PROCS or \ + idx == StatsDialog.TAB_ADDRS or \ + idx == StatsDialog.TAB_PORTS or \ + idx == StatsDialog.TAB_USERS: + return " WHERE what LIKE '%{0}%' ".format(text) + + return "" + + def _get_limit(self): + return " " + self.LIMITS[self.limitCombo.currentIndex()] + + def _get_order(self, field=None): + cur_idx = self.tabWidget.currentIndex() + order_field = self.TABLES[cur_idx]['last_order_by'] + if field != None: + order_field = field + return " ORDER BY %s %s" % (order_field, self.SORT_ORDER[self.TABLES[cur_idx]['last_order_to']]) + + def _refresh_active_table(self): + cur_idx = self.tabWidget.currentIndex() + model = self._get_active_table().model() + lastQuery = model.query().lastQuery() + if "LIMIT" not in lastQuery: + lastQuery += self._get_limit() + self.setQuery(model, lastQuery) + self.TABLES[cur_idx]['view'].refresh() + + def _get_active_table(self): + if self.tabWidget.currentIndex() == self.TAB_RULES and self.fwTable.isVisible(): + return self.TABLES[self.TAB_FIREWALL]['view'] + return self.TABLES[self.tabWidget.currentIndex()]['view'] + + def _set_active_widgets(self, prev_idx, state, label_txt=""): + cur_idx = self.tabWidget.currentIndex() + self._clear_rows_selection() + self.TABLES[cur_idx]['label'].setVisible(state) + self.TABLES[cur_idx]['label'].setText(label_txt) + self.TABLES[cur_idx]['cmd'].setVisible(state) + + if self.TABLES[cur_idx]['filterLine'] != None: + self.TABLES[cur_idx]['filterLine'].setVisible(not state) + + if self.TABLES[cur_idx].get('cmdCleanStats') != None: + if cur_idx == StatsDialog.TAB_RULES or cur_idx == StatsDialog.TAB_NODES: + self.TABLES[cur_idx]['cmdCleanStats'].setVisible(state) + + if cur_idx == StatsDialog.TAB_NODES: + self._update_nodes_interception_status(state) + self.nodeDeleteButton.setVisible(state) + self.nodeActionsButton.setVisible(state) + + header = self.TABLES[cur_idx]['view'].horizontalHeader() + if state == True: + # going to details state + self._cfg.setSettings("{0}{1}".format(Config.STATS_VIEW_COL_STATE, prev_idx), header.saveState()) + else: + # going to normal state + self._cfg.setSettings("{0}{1}".format(Config.STATS_VIEW_DETAILS_COL_STATE, cur_idx), header.saveState()) + + def _restore_last_selected_row(self): + cur_idx = self.tabWidget.currentIndex() + col = self.COL_TIME + if cur_idx == self.TAB_RULES: + col = self.TAB_RULES + elif cur_idx == self.TAB_NODES: + col = self.TAB_RULES + + #self.TABLES[cur_idx]['view'].selectItem(self.LAST_SELECTED_ITEM, col) + self.LAST_SELECTED_ITEM = "" + + def _restore_scroll_value(self): + if self.LAST_SCROLL_VALUE != None: + cur_idx = self.tabWidget.currentIndex() + self.TABLES[cur_idx]['view'].vScrollBar.setValue(self.LAST_SCROLL_VALUE) + self.LAST_SCROLL_VALUE = None + + def _restore_details_view_columns(self, header, settings_key): + header.blockSignals(True); + # In order to resize the last column of a view, we firstly force a + # resizeToContens call. + # Secondly set resizeMode to Interactive (allow to move columns by + # users + programmatically) + header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents) + header.setSectionResizeMode(QtWidgets.QHeaderView.Interactive) + + col_state = self._cfg.getSettings(settings_key) + if type(col_state) == QtCore.QByteArray: + header.restoreState(col_state) + + header.blockSignals(False); + + def _restore_rules_tab_widgets(self, active): + self.delRuleButton.setVisible(not active) + self.editRuleButton.setVisible(not active) + self.nodeRuleLabel.setText("") + self.rulesTreePanel.setVisible(active) + + if not active: + return + + self.rulesSplitter.refresh() + self.comboRulesFilter.setVisible(self.rulesTreePanel.width() == 0) + + items = self.rulesTreePanel.selectedItems() + if len(items) == 0: + self._set_rules_filter() + return + + rindex = item_m = self.rulesTreePanel.indexFromItem(items[0], 0) + parent = item_m.parent() + + # find current root item of the tree panel + while rindex.parent().isValid(): + rindex = rindex.parent() + rnum = rindex.row() + + if parent != None and rnum != self.RULES_TREE_FIREWALL: + self._set_rules_filter(parent.row(), item_m.row(), item_m.data()) + else: + # when going back to the rules view, reset selection and select the + # Apps view. + index = self.rulesTreePanel.model().index(self.RULES_TREE_APPS, 0) + self.rulesTreePanel.setCurrentIndex(index) + self._set_rules_filter() + + + def _set_rules_tab_active(self, row, cur_idx, name_idx, node_idx): + self._restore_rules_tab_widgets(False) + self.comboRulesFilter.setVisible(False) + + r_name = row.model().index(row.row(), name_idx).data() + node = row.model().index(row.row(), node_idx).data() + self.nodeRuleLabel.setText(node) + + self.fwTable.setVisible(False) + self.rulesTable.setVisible(True) + self.tabWidget.setCurrentIndex(cur_idx) + + return r_name, node + + def _set_events_query(self): + if self.tabWidget.currentIndex() != self.TAB_MAIN: + return + + model = self.TABLES[self.TAB_MAIN]['view'].model() + qstr = self._db.get_query(self.TABLES[self.TAB_MAIN]['name'], self.TABLES[self.TAB_MAIN]['display_fields']) + + filter_text = self.filterLine.text() + action = "" + if self.comboAction.currentIndex() == 1: + action = "action = \"{0}\"".format(Config.ACTION_ALLOW) + elif self.comboAction.currentIndex() == 2: + action = "action = \"{0}\"".format(Config.ACTION_DENY) + elif self.comboAction.currentIndex() == 3: + action = "action = \"{0}\"".format(Config.ACTION_REJECT) + + # FIXME: use prepared statements + if filter_text == "": + if action != "": + qstr += " WHERE " + action + else: + if action != "": + action += " AND " + qstr += " WHERE " + action + " ("\ + " process LIKE '%" + filter_text + "%'" \ + " OR process_args LIKE '%" + filter_text + "%'" \ + " OR src_port LIKE '%" + filter_text + "%'" \ + " OR src_ip LIKE '%" + filter_text + "%'" \ + " OR dst_ip LIKE '%" + filter_text + "%'" \ + " OR dst_host LIKE '%" + filter_text + "%'" \ + " OR dst_port LIKE '%" + filter_text + "%'" \ + " OR rule LIKE '%" + filter_text + "%'" \ + " OR node LIKE '%" + filter_text + "%'" \ + " OR time LIKE '%" + filter_text + "%'" \ + " OR pid LIKE '%" + filter_text + "%'" \ + " OR uid LIKE '%" + filter_text + "%'" \ + " OR protocol LIKE '%" + filter_text + "%')" \ + + qstr += self._get_order() + self._get_limit() + self.setQuery(model, qstr) + + def _set_nodes_query(self, data): + + model = self._get_active_table().model() + self.setQuery(model, "SELECT " \ + "MAX(c.time) as {0}, " \ + "c.action as {1}, " \ + "count(c.process) as {2}, " \ + "c.uid as {3}, " \ + "c.protocol as {4}, " \ + "c.src_port as {5}, " \ + "c.src_ip as {6}, " \ + "c.dst_ip as {7}, " \ + "c.dst_host as {8}, " \ + "c.dst_port as {9}, " \ + "c.pid as {10}, " \ + "c.process as {11}, " \ + "c.process_args as {12}, " \ + "c.process_cwd as CWD, " \ + "c.rule as {13} " \ + "FROM connections as c " \ + "WHERE c.node = '{14}' GROUP BY {15}, c.process_args, c.uid, c.src_ip, c.dst_ip, c.dst_host, c.dst_port, c.protocol {16}".format( + self.COL_STR_TIME, + self.COL_STR_ACTION, + self.COL_STR_HITS, + self.COL_STR_UID, + self.COL_STR_PROTOCOL, + self.COL_STR_SRC_PORT, + self.COL_STR_SRC_IP, + self.COL_STR_DST_IP, + self.COL_STR_DST_HOST, + self.COL_STR_DST_PORT, + self.COL_STR_PID, + self.COL_STR_PROCESS, + self.COL_STR_PROC_CMDLINE, + self.COL_STR_RULE, + data, + self.COL_STR_PROCESS, + self._get_order() + self._get_limit())) + + def _get_nodes_filter_query(self, lastQuery, text): + base_query = lastQuery.split("GROUP BY") + if not self.IN_DETAIL_VIEW[self.TAB_NODES]: + base_query = lastQuery.split("ORDER BY") + + qstr = base_query[0] + if "AND" in qstr: + # strip out ANDs if any + os = qstr.split('AND') + qstr = os[0] + + if text != "": + if self.IN_DETAIL_VIEW[self.TAB_NODES]: + qstr += "AND (c.time LIKE '%{0}%' OR " \ + "c.action LIKE '%{0}%' OR " \ + "c.uid LIKE '%{0}%' OR " \ + "c.pid LIKE '%{0}%' OR " \ + "c.src_port LIKE '%{0}%' OR " \ + "c.dst_port LIKE '%{0}%' OR " \ + "c.src_ip LIKE '%{0}%' OR " \ + "c.dst_ip LIKE '%{0}%' OR " \ + "c.dst_host LIKE '%{0}%' OR " \ + "c.process LIKE '%{0}%' OR " \ + "c.process_cwd LIKE '%{0}%' OR " \ + "c.process_args LIKE '%{0}%')".format(text) + else: + if "WHERE" in qstr: + w = qstr.split('WHERE') + qstr = w[0] + + qstr += "WHERE (" \ + "last_connection LIKE '%{0}%' OR " \ + "addr LIKE '%{0}%' OR " \ + "status LIKE '%{0}%' OR " \ + "hostname LIKE '%{0}%' OR " \ + "version LIKE '%{0}%'" \ + ")".format(text) + + if self.IN_DETAIL_VIEW[self.TAB_NODES]: + qstr += " GROUP BY" + base_query[1] + else: + qstr += " ORDER BY" + base_query[1] + + return qstr + + def _update_nodes_interception_status(self, show=True, disable=False): + addr = self.TABLES[self.TAB_NODES]['label'].text() + node_cfg = self._nodes.get_node(addr) + if node_cfg == None: + self.nodeStartButton.setVisible(False) + self.nodePrefsButton.setVisible(False) + self.nodeDeleteButton.setVisible(False) + self.nodeActionsButton.setVisible(False) + return + self.nodeStartButton.setVisible(show) + self.nodePrefsButton.setVisible(show) + self.nodeActionsButton.setVisible(show) + if not node_cfg['data'].isFirewallRunning or disable: + self.nodeStartButton.setChecked(False) + self.nodeStartButton.setDown(False) + self.nodeStartButton.setIcon(self.iconStart) + else: + self.nodeStartButton.setIcon(self.iconPause) + self.nodeStartButton.setChecked(True) + self.nodeStartButton.setDown(True) + + def _set_rules_filter(self, parent_row=-1, item_row=0, what="", what1="", what2=""): + section = self.FILTER_TREE_APPS + + if parent_row == -1: + self.fwTable.setVisible(item_row == self.RULES_TREE_FIREWALL) + self.rulesTable.setVisible(item_row != self.RULES_TREE_FIREWALL) + if item_row == self.RULES_TREE_NODES: + section=self.FILTER_TREE_NODES + what="" + elif item_row == self.RULES_TREE_FIREWALL: + self.TABLES[self.TAB_FIREWALL]['view'].model().filterAll() + return + else: + section=self.FILTER_TREE_APPS + what="" + + elif parent_row == self.RULES_TREE_APPS: + if item_row == self.RULES_TREE_PERMANENT: + section=self.FILTER_TREE_APPS + what=self.RULES_TYPE_PERMANENT + elif item_row == self.RULES_TREE_TEMPORARY: + section=self.FILTER_TREE_APPS + what=self.RULES_TYPE_TEMPORARY + + elif parent_row == self.RULES_TREE_NODES: + section=self.FILTER_TREE_NODES + + elif parent_row == self.RULES_TREE_FIREWALL: + if item_row == self.FILTER_TREE_FW_NODE: + self.TABLES[self.TAB_FIREWALL]['view'].filterByNode(what) + elif item_row == self.FILTER_TREE_FW_TABLE: + parm = what.split("-") + if len(parm) < 2: + return + self.TABLES[self.TAB_FIREWALL]['view'].filterByTable(what1, parm[0], parm[1]) + elif item_row == self.FILTER_TREE_FW_CHAIN: # + table + parm = what.split("-") + tbl = what1.split("-") + self.TABLES[self.TAB_FIREWALL]['view'].filterByChain(what2, tbl[0], tbl[1], parm[0], parm[1]) + return + + if section == self.FILTER_TREE_APPS: + if what == self.RULES_TYPE_TEMPORARY: + what = "WHERE r.duration != '%s'" % Config.DURATION_ALWAYS + elif what == self.RULES_TYPE_PERMANENT: + what = "WHERE r.duration = '%s'" % Config.DURATION_ALWAYS + elif section == self.FILTER_TREE_NODES and what != "": + what = "WHERE r.node = '%s'" % what + + filter_text = self.filterLine.text() + if filter_text != "": + if what == "": + what = "WHERE" + else: + what = what + " AND" + what = what + " r.name LIKE '%{0}%'".format(filter_text) + model = self._get_active_table().model() + self.setQuery(model, "SELECT {0} FROM rules as r {1} {2} {3}".format( + self.TABLES[self.TAB_RULES]['display_fields'], + what, + self._get_order(), + self._get_limit() + )) + + def _set_rules_query(self, rule_name="", node=""): + if node != "": + node = "c.node = '%s'" % node + if rule_name != "": + rule_name = "c.rule = '%s'" % rule_name + + condition = "%s AND %s" % (rule_name, node) if rule_name != "" and node != "" else "" + + model = self._get_active_table().model() + self.setQuery(model, "SELECT " \ + "MAX(c.time) as {0}, " \ + "c.node as {1}, " \ + "count(c.process) as {2}, " \ + "c.uid as {3}, " \ + "c.protocol as {4}, " \ + "c.src_port as {5}, " \ + "c.src_ip as {6}, " \ + "c.dst_ip as {7}, " \ + "c.dst_host as {8}, " \ + "c.dst_port as {9}, " \ + "c.pid as {10}, " \ + "c.process as {11}, " \ + "c.process_args as {12}, " \ + "c.process_cwd as CWD " \ + "FROM connections as c " \ + "WHERE {13} GROUP BY c.process, c.process_args, c.uid, c.dst_ip, c.dst_host, c.dst_port {14}".format( + self.COL_STR_TIME, + self.COL_STR_NODE, + self.COL_STR_HITS, + self.COL_STR_UID, + self.COL_STR_PROTOCOL, + self.COL_STR_SRC_PORT, + self.COL_STR_SRC_IP, + self.COL_STR_DST_IP, + self.COL_STR_DST_HOST, + self.COL_STR_DST_PORT, + self.COL_STR_PID, + self.COL_STR_PROCESS, + self.COL_STR_PROC_CMDLINE, + condition, + self._get_order() + self._get_limit() + )) + + def _set_hosts_query(self, data): + model = self._get_active_table().model() + self.setQuery(model, "SELECT " \ + "MAX(c.time) as {0}, " \ + "c.node as {1}, " \ + "count(c.process) as {2}, " \ + "c.action as {3}, " \ + "c.uid as {4}, " \ + "c.protocol as {5}, " \ + "c.src_port as {6}, " \ + "c.src_ip as {7}, " \ + "c.dst_ip as {9}, " \ + "c.dst_port as {8}, " \ + "c.pid as {10}, " \ + "c.process as {11}, " \ + "c.process_args as {12}, " \ + "c.process_cwd as CWD, " \ + "c.rule as {13} " \ + "FROM connections as c " \ + "WHERE c.dst_host = '{14}' GROUP BY c.pid, {15}, c.process_args, c.src_ip, c.dst_ip, c.dst_port, c.protocol, c.action, c.node {16}".format( + self.COL_STR_TIME, + self.COL_STR_NODE, + self.COL_STR_HITS, + self.COL_STR_ACTION, + self.COL_STR_UID, + self.COL_STR_PROTOCOL, + self.COL_STR_SRC_PORT, + self.COL_STR_SRC_IP, + self.COL_STR_DST_IP, + self.COL_STR_DST_PORT, + self.COL_STR_PID, + self.COL_STR_PROCESS, + self.COL_STR_PROC_CMDLINE, + self.COL_STR_RULE, + data, + self.COL_STR_PROCESS, + self._get_order("1") + self._get_limit())) + + def _set_process_query(self, data): + model = self._get_active_table().model() + self.setQuery(model, "SELECT " \ + "MAX(c.time) as {0}, " \ + "c.node as {1}, " \ + "count(c.dst_ip) as {2}, " \ + "c.action as {3}, " \ + "c.uid as {4}, " \ + "c.protocol as {5}, " \ + "c.src_port as {6}, " \ + "c.src_ip as {7}, " \ + "c.dst_ip as {8}, " \ + "c.dst_host as {9}, " \ + "c.dst_port as {10}, " \ + "c.pid as PID, " \ + "c.process_args as {11}, " \ + "c.process_cwd as CWD, " \ + "c.rule as {12} " \ + "FROM connections as c " \ + "WHERE c.process = '{13}' " \ + "GROUP BY c.src_ip, c.dst_ip, c.dst_host, c.dst_port, c.uid, c.action, c.node, c.pid, c.process_args {14}".format( + self.COL_STR_TIME, + self.COL_STR_NODE, + self.COL_STR_HITS, + self.COL_STR_ACTION, + self.COL_STR_UID, + self.COL_STR_PROTOCOL, + self.COL_STR_SRC_PORT, + self.COL_STR_SRC_IP, + self.COL_STR_DST_IP, + self.COL_STR_DST_HOST, + self.COL_STR_DST_PORT, + self.COL_STR_PROC_CMDLINE, + self.COL_STR_RULE, + data, + self._get_order("1") + self._get_limit())) + + nrows = self._get_active_table().model().rowCount() + self.cmdProcDetails.setVisible(nrows != 0) + + def _set_addrs_query(self, data): + model = self._get_active_table().model() + self.setQuery(model, "SELECT " \ + "MAX(c.time) as {0}, " \ + "c.node as {1}, " \ + "count(c.dst_ip) as {2}, " \ + "c.action as {3}, " \ + "c.uid as {4}, " \ + "c.protocol as {5}, " \ + "c.src_port as {6}, " \ + "c.src_ip as {7}, " \ + "c.dst_host as {8}, " \ + "c.dst_port as {9}, " \ + "c.pid as {10}, " \ + "c.process as {11}, " \ + "c.process_args as {12}, " \ + "c.process_cwd as CWD, " \ + "c.rule as {13} " \ + "FROM connections as c " \ + "WHERE c.dst_ip = '{14}' GROUP BY c.pid, {15}, c.process_args, c.src_ip, c.dst_port, {16}, c.protocol, c.action, c.uid, c.node {17}".format( + self.COL_STR_TIME, + self.COL_STR_NODE, + self.COL_STR_HITS, + self.COL_STR_ACTION, + self.COL_STR_UID, + self.COL_STR_PROTOCOL, + self.COL_STR_SRC_PORT, + self.COL_STR_SRC_IP, + self.COL_STR_DST_HOST, + self.COL_STR_DST_PORT, + self.COL_STR_PID, + self.COL_STR_PROCESS, + self.COL_STR_PROC_CMDLINE, + self.COL_STR_RULE, + data, + self.COL_STR_PROCESS, + self.COL_STR_DST_HOST, + self._get_order("1") + self._get_limit())) + + def _set_ports_query(self, data): + model = self._get_active_table().model() + self.setQuery(model, "SELECT " \ + "MAX(c.time) as {0}, " \ + "c.node as {1}, " \ + "count(c.dst_ip) as {2}, " \ + "c.action as {3}, " \ + "c.uid as {4}, " \ + "c.protocol as {5}, " \ + "c.src_port as {6}, " \ + "c.src_ip as {7}, " \ + "c.dst_ip as {8}, " \ + "c.dst_host as {9}, " \ + "c.pid as {10}, " \ + "c.process as {11}, " \ + "c.process_args as {12}, " \ + "c.process_cwd as CWD, " \ + "c.rule as {13} " \ + "FROM connections as c " \ + "WHERE c.dst_port = '{14}' GROUP BY c.pid, {15}, c.process_args, {16}, c.src_ip, c.dst_ip, c.protocol, c.action, c.uid, c.node {17}".format( + self.COL_STR_TIME, + self.COL_STR_NODE, + self.COL_STR_HITS, + self.COL_STR_ACTION, + self.COL_STR_UID, + self.COL_STR_PROTOCOL, + self.COL_STR_SRC_PORT, + self.COL_STR_SRC_IP, + self.COL_STR_DST_IP, + self.COL_STR_DST_HOST, + self.COL_STR_PID, + self.COL_STR_PROCESS, + self.COL_STR_PROC_CMDLINE, + self.COL_STR_RULE, + data, + self.COL_STR_PROCESS, + self.COL_STR_DST_HOST, + self._get_order("1") + self._get_limit())) + + def _set_users_query(self, data): + uid = data.split(" ") + if len(uid) == 2: + uid = uid[1].strip("()") + else: + uid = uid[0] + model = self._get_active_table().model() + self.setQuery(model, "SELECT " \ + "MAX(c.time) as {0}, " \ + "c.node as {1}, " \ + "count(c.dst_ip) as {2}, " \ + "c.action as {3}, " \ + "c.protocol as {4}, " \ + "c.src_port as {5}, " \ + "c.src_ip as {6}, " \ + "c.dst_ip as {7}, " \ + "c.dst_host as {8}, " \ + "c.dst_port as {9}, " \ + "c.pid as {10}, " \ + "c.process as {11}, " \ + "c.process_args as {12}, " \ + "c.process_cwd as CWD, " \ + "c.rule as {13} " \ + "FROM connections as c " \ + "WHERE c.uid = '{14}' GROUP BY c.pid, {15}, c.process_args, c.src_ip, c.dst_ip, c.dst_host, c.dst_port, c.protocol, c.action, c.node {16}".format( + self.COL_STR_TIME, + self.COL_STR_NODE, + self.COL_STR_HITS, + self.COL_STR_ACTION, + self.COL_STR_PROTOCOL, + self.COL_STR_SRC_PORT, + self.COL_STR_SRC_IP, + self.COL_STR_DST_IP, + self.COL_STR_DST_HOST, + self.COL_STR_DST_PORT, + self.COL_STR_PID, + self.COL_STR_PROCESS, + self.COL_STR_PROC_CMDLINE, + self.COL_STR_RULE, + uid, + self.COL_STR_PROCESS, + self._get_order("1") + self._get_limit())) + + # get the query filtering by text when a tab is in the detail view. + def _get_indetail_filter_query(self, lastQuery, text): + try: + cur_idx = self.tabWidget.currentIndex() + base_query = lastQuery.split("GROUP BY") + qstr = base_query[0] + where = qstr.split("WHERE")[1] # get SELECT ... WHERE (*) + ands = where.split("AND (")[0] # get WHERE (*) AND (...) + qstr = qstr.split("WHERE")[0] # get * WHERE ... + qstr += "WHERE %s" % ands + + # if there's no text to filter, strip the filter "AND ()", and + # return the original query. + if text == "": + return + + qstr += "AND (c.time LIKE '%{0}%' OR " \ + "c.action LIKE '%{0}%' OR " \ + "c.pid LIKE '%{0}%' OR " \ + "c.protocol LIKE '%{0}%' OR " \ + "c.src_port LIKE '%{0}%' OR " \ + "c.src_ip LIKE '%{0}%' OR ".format(text) + + # exclude from query the field of the view we're filtering by + if self.IN_DETAIL_VIEW[cur_idx] != self.TAB_PORTS: + qstr += "c.dst_port LIKE '%{0}%' OR ".format(text) + if self.IN_DETAIL_VIEW[cur_idx] != self.TAB_ADDRS: + qstr += "c.dst_ip LIKE '%{0}%' OR ".format(text) + if self.IN_DETAIL_VIEW[cur_idx] != self.TAB_HOSTS: + qstr += "c.dst_host LIKE '%{0}%' OR ".format(text) + if self.IN_DETAIL_VIEW[cur_idx] != self.TAB_PROCS: + qstr += "c.process LIKE '%{0}%' OR ".format(text) + + qstr += "c.process_args LIKE '%{0}%')".format(text) + + finally: + if len(base_query) > 1: + qstr += " GROUP BY" + base_query[1] + return qstr + + @QtCore.pyqtSlot() + def _on_settings_saved(self): + self._ui_refresh_interval = self._cfg.getInt(Config.STATS_REFRESH_INTERVAL, 0) + self._show_columns() + self.settings_saved.emit() + + def _on_menu_node_export_clicked(self, triggered): + outdir = QtWidgets.QFileDialog.getExistingDirectory(self, + os.path.expanduser("~"), + QC.translate("stats", 'Select a directory to export rules'), + QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontResolveSymlinks) + if outdir == "": + return + + node = self.nodesLabel.text() + if self._nodes.export_rules(node, outdir) == False: + Message.ok("Rules export error", + QC.translate("stats", + "Error exporting rules" + ), + QtWidgets.QMessageBox.Warning) + else: + Message.ok("Rules export", + QC.translate("stats", "Rules exported to {0}".format(outdir)), + QtWidgets.QMessageBox.Information) + + + def _on_menu_node_import_clicked(self, triggered): + rulesdir = QtWidgets.QFileDialog.getExistingDirectory(self, + os.path.expanduser("~"), + QC.translate("stats", 'Select a directory with rules to import (JSON files)'), + QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontResolveSymlinks) + if rulesdir == '': + return + + node = self.nodesLabel.text() + nid, notif, rules = self._nodes.import_rules(addr=node, rulesdir=rulesdir, callback=self._notification_callback) + if nid != None: + self._notifications_sent[nid] = notif + # TODO: add rules per node and after receiving the notification + for node in self._nodes.get_nodes(): + self._nodes.add_rules(node, rules) + + Message.ok("Rules import", + QC.translate("stats", "Rules imported fine"), + QtWidgets.QMessageBox.Information) + if self.tabWidget.currentIndex() == self.TAB_RULES: + self._refresh_active_table() + else: + Message.ok("Rules import error", + QC.translate("stats", + "Error importing rules from {0}".format(rulesdir) + ), + QtWidgets.QMessageBox.Warning) + + + + def _on_menu_exit_clicked(self, triggered): + self.close_trigger.emit() + + def _on_menu_export_clicked(self, triggered): + outdir = QtWidgets.QFileDialog.getExistingDirectory(self, + os.path.expanduser("~"), + QC.translate("stats", 'Select a directory to export rules'), + QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontResolveSymlinks) + if outdir == "": + return + + errors = [] + for node in self._nodes.get_nodes(): + if self._nodes.export_rules(node, outdir) == False: + errors.append(node) + # apply_to_node()... + + if len(errors) > 0: + errorlist = "" + for e in errors: + errorlist = errorlist + e + "<br>" + Message.ok("Rules export error", + QC.translate("stats", + "Error exporting rules of the following nodes:<br><br>{0}" + .format(errorlist) + ), + QtWidgets.QMessageBox.Warning) + else: + Message.ok("Rules export", + QC.translate("stats", "Rules exported to {0}".format(outdir)), + QtWidgets.QMessageBox.Information) + + def _on_menu_import_clicked(self, triggered): + rulesdir = QtWidgets.QFileDialog.getExistingDirectory(self, + os.path.expanduser("~"), + QC.translate("stats", 'Select a directory with rules to import (JSON files)'), + QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontResolveSymlinks) + if rulesdir == '': + return + + nid, notif, rules = self._nodes.import_rules(rulesdir=rulesdir, callback=self._notification_callback) + if nid != None: + self._notifications_sent[nid] = notif + # TODO: add rules per node and after receiving the notification + for node in self._nodes.get_nodes(): + self._nodes.add_rules(node, rules) + + Message.ok("Rules import", + QC.translate("stats", "Rules imported fine"), + QtWidgets.QMessageBox.Information) + if self.tabWidget.currentIndex() == self.TAB_RULES: + self._refresh_active_table() + else: + Message.ok("Rules import error", + QC.translate("stats", + "Error importing rules from {0}".format(rulesdir) + ), + QtWidgets.QMessageBox.Warning) + + def _on_menu_export_csv_clicked(self, triggered): + tab_idx = self.tabWidget.currentIndex() + + filename = QtWidgets.QFileDialog.getSaveFileName(self, + QC.translate("stats", 'Save as CSV'), + self._file_names[tab_idx], + 'All Files (*);;CSV Files (*.csv)')[0].strip() + if filename == '': + return + + with self._lock: + table = self._tables[tab_idx] + ncols = table.model().columnCount() + nrows = table.model().rowCount() + cols = [] + + for col in range(0, ncols): + cols.append(table.model().headerData(col, QtCore.Qt.Horizontal)) + + with open(filename, 'w') as csvfile: + w = csv.writer(csvfile, dialect='excel') + w.writerow(cols) + + if tab_idx == self.TAB_MAIN: + w.writerows(table.model().dumpRows()) + else: + for row in range(0, nrows): + values = [] + for col in range(0, ncols): + values.append(table.model().index(row, col).data()) + w.writerow(values) + + def _setup_table(self, widget, tableWidget, table_name, fields="*", group_by="", order_by="2", sort_direction=SORT_ORDER[1], limit="", resize_cols=(), model=None, delegate=None, verticalScrollBar=None, tracking_column=COL_TIME): + tableWidget.setSortingEnabled(True) + if model == None: + model = self._db.get_new_qsql_model() + if verticalScrollBar != None: + tableWidget.setVerticalScrollBar(verticalScrollBar) + tableWidget.verticalScrollBar().sliderPressed.connect(self._cb_scrollbar_pressed) + tableWidget.verticalScrollBar().sliderReleased.connect(self._cb_scrollbar_released) + tableWidget.setTrackingColumn(tracking_column) + + self.setQuery(model, "SELECT " + fields + " FROM " + table_name + group_by + " ORDER BY " + order_by + " " + sort_direction + limit) + tableWidget.setModel(model) + + if delegate != None: + action = self._actions.get(delegate) + if action != None: + tableWidget.setItemDelegate(ColorizedDelegate(tableWidget, actions=action)) + + header = tableWidget.horizontalHeader() + if header != None: + header.sortIndicatorChanged.connect(self._cb_table_header_clicked) + + for _, col in enumerate(resize_cols): + header.setSectionResizeMode(col, QtWidgets.QHeaderView.ResizeToContents) + + cur_idx = self.tabWidget.currentIndex() + self._cfg.setSettings("{0}{1}".format(Config.STATS_VIEW_DETAILS_COL_STATE, cur_idx), header.saveState()) + return tableWidget + + def update_interception_status(self, enabled): + self.startButton.setDown(enabled) + self.startButton.setChecked(enabled) + if enabled: + self._update_status_label(running=True, text=self.FIREWALL_RUNNING) + else: + self._update_status_label(running=False, text=self.FIREWALL_DISABLED) + + def _needs_refresh(self): + diff = datetime.datetime.now() - self._last_update + if diff.seconds < self._ui_refresh_interval: + return False + + return True + + # launched from a thread + def update(self, is_local=True, stats=None, need_query_update=True): + # lock mandatory when there're multiple clients + with self._lock: + if stats is not None: + self._stats = stats + # do not update any tab if the window is not visible + if self.isVisible() and self.isMinimized() == False and self._needs_refresh(): + self._trigger.emit(is_local, need_query_update) + self._last_update = datetime.datetime.now() + + def update_status(self): + self.startButton.setDown(self.daemon_connected) + self.startButton.setChecked(self.daemon_connected) + self.startButton.setDisabled(not self.daemon_connected) + if self.daemon_connected: + self._update_status_label(running=True, text=self.FIREWALL_RUNNING) + else: + self._update_status_label(running=False, text=self.FIREWALL_STOPPED) + self.statusLabel.setStyleSheet('color: red; margin: 5px') + + @QtCore.pyqtSlot(bool, bool) + def _on_update_triggered(self, is_local, need_query_update=False): + if self._stats is None: + self.daemonVerLabel.setText("") + self.uptimeLabel.setText("") + self.rulesLabel.setText("") + self.consLabel.setText("") + self.droppedLabel.setText("") + else: + nodes = self._nodes.count() + self.daemonVerLabel.setText(self._stats.daemon_version) + if nodes <= 1: + self.uptimeLabel.setText(str(datetime.timedelta(seconds=self._stats.uptime))) + self.rulesLabel.setText("%s" % self._stats.rules) + self.consLabel.setText("%s" % self._stats.connections) + self.droppedLabel.setText("%s" % self._stats.dropped) + else: + self.uptimeLabel.setText("") + self.rulesLabel.setText("") + self.consLabel.setText("") + self.droppedLabel.setText("") + + if need_query_update and not self._are_rows_selected(): + self._refresh_active_table() + + # prevent a click on the window's x + # from quitting the whole application + def closeEvent(self, e): + self._save_settings() + e.accept() + self.hide() + + def hideEvent(self, e): + self._save_settings() + + # https://gis.stackexchange.com/questions/86398/how-to-disable-the-escape-key-for-a-dialog + def keyPressEvent(self, event): + if not event.key() == QtCore.Qt.Key_Escape: + super(StatsDialog, self).keyPressEvent(event) + + def setQuery(self, model, q): + if self._context_menu_active == True or self.scrollbar_active == True: + return + with self._lock: + try: + model.query().clear() + model.setQuery(q, self._db_sqlite) + if model.lastError().isValid(): + print("setQuery() error: ", model.lastError().text()) + + if self.tabWidget.currentIndex() != self.TAB_MAIN: + self.labelRowsCount.setText("{0}".format(model.totalRowCount)) + else: + self.labelRowsCount.setText("") + except Exception as e: + print(self._address, "setQuery() exception: ", e) diff --git a/ui/opensnitch/firewall/__init__.py b/ui/opensnitch/firewall/__init__.py new file mode 100644 index 0000000..ee3dac4 --- /dev/null +++ b/ui/opensnitch/firewall/__init__.py @@ -0,0 +1,233 @@ +from PyQt5.QtCore import QObject, QCoreApplication as QC +from google.protobuf import json_format +import opensnitch.proto as proto +ui_pb2, ui_pb2_grpc = proto.import_() + +from opensnitch.nodes import Nodes +from .enums import * +from .rules import * +from .chains import * +from .utils import Utils +from .exprs import * +from .profiles import * + +class Firewall(QObject): + __instance = None + + @staticmethod + def instance(): + if Firewall.__instance == None: + Firewall.__instance = Firewall() + return Firewall.__instance + + def __init__(self, parent=None): + QObject.__init__(self) + self._nodes = Nodes.instance() + self.rules = Rules(self._nodes) + self.chains = Chains(self._nodes) + + def switch_rules(self, key, old_pos, new_pos): + pass + + def add_rule(self, addr, rule): + return self.rules.add(addr, rule) + + def insert_rule(self, addr, rule, position=0): + return self.rules.insert(addr, rule, position) + + def update_rule(self, addr, uuid, rule): + return self.rules.update(addr, uuid, rule) + + def delete_rule(self, addr, uuid): + return self.rules.delete(addr, uuid) + + def change_rule_field(self, addr, uuid, field, value): + addr, chain = self.get_rule_by_uuid(uuid) + if chain is None: + return None, None + + if field == Rules.FIELD_ENABLED: + chain.Rules[0].Enabled = value + elif field == Rules.FIELD_TARGET: + chain.Rules[0].Target = value + return self.update_rule(addr, uuid, chain) + + def enable_rule(self, addr, uuid, enable): + addr, chain = self.get_rule_by_uuid(uuid) + if chain is None: + return None, None + + chain.Rules[0].Enabled = enable + return self.update_rule(addr, uuid, chain) + + def filter_rules(self, nail): + """ + """ + chains = [] + for addr in self._nodes.get_nodes(): + node = self._nodes.get_node(addr) + if not 'firewall' in node: + return chains + for n in node['firewall'].SystemRules: + for c in n.Chains: + for r in c.Rules: + add_rule = False + if nail == r.UUID: + add_rule = True + + if nail in c.Family or \ + nail in c.Hook or \ + nail in r.Description or \ + nail in r.Target or \ + nail in r.TargetParameters: + add_rule = True + else: + for e in r.Expressions: + if add_rule: + break + expr_vals = "".join("{0} {1}".format(h.Key, h.Value) for h in e.Statement.Values) + #print(nail in expr_vals, r.Description) + if nail in e.Statement.Op or \ + nail in e.Statement.Name or \ + nail in e.Statement.Values or \ + nail in expr_vals: + add_rule = True + + if add_rule: + chains.append(Rules.to_array(addr, c, r)) + + return chains + + def filter_by_table(self, addr, table, family): + """get rules by table""" + chains = [] + node = self._nodes.get_node(addr) + if not 'firewall' in node: + return chains + for n in node['firewall'].SystemRules: + for c in n.Chains: + for r in c.Rules: + if c.Table == table and c.Family == family: + chains.append(Rules.to_array(addr, c, r)) + + return chains + + def filter_by_chain(self, addr, table, family, chain, hook): + """get rules by chain""" + chains = [] + node = self._nodes.get_node(addr) + if not 'firewall' in node: + return chains + for n in node['firewall'].SystemRules: + for c in n.Chains: + for r in c.Rules: + if c.Table == table and c.Family == family and c.Name == chain and c.Hook == hook: + chains.append(Rules.to_array(addr, c, r)) + + return chains + + def swap_rules(self, view, addr, uuid, old_pos, new_pos): + return self.rules.swap(view, addr, uuid, old_pos, new_pos) + + def get_rule_by_uuid(self, uuid): + """get rule by uuid, in string format + """ + if uuid == "": + return None, None + for addr in self._nodes.get_nodes(): + node = self._nodes.get_node(addr) + if not 'fwrules' in node: + continue + r = node['fwrules'].get(uuid) + if r != None: + return addr, r + + return None, None + + def get_protorule_by_uuid(self, addr, uuid): + """get protobuffer rule by uuid. + """ + return self.rules.get_by_uuid(addr, uuid) + + def get_node_rules(self, addr): + return self.rules.get_by_node(addr) + + def get_chains(self): + return self.chains.get() + + def get_rules(self): + return self.rules.get() + + def rule_to_json(self, rule): + return Rules.to_json(rule) + + def apply_profile(self, node_addr, json_profile): + """ + Apply a profile to the firewall configuration. + + Given a chain (table+family+type+hook), apply its policy, and any rules + defined. + """ + try: + holder = ui_pb2.FwChain() + profile = json_format.Parse(json_profile, holder) + + fwcfg = self._nodes.get_node(node_addr)['firewall'] + for sdx, n in enumerate(fwcfg.SystemRules): + for cdx, c in enumerate(n.Chains): + + if c.Hook.lower() == profile.Hook and \ + c.Type.lower() == profile.Type and \ + c.Family.lower() == profile.Family and \ + c.Table.lower() == profile.Table: + + fwcfg.SystemRules[sdx].Chains[cdx].Policy = profile.Policy + for r in profile.Rules: + temp_c = ui_pb2.FwChain() + temp_c.CopyFrom(c) + del temp_c.Rules[:] + temp_c.Rules.extend([r]) + + if self.rules.is_duplicated(node_addr, temp_c): + continue + self.add_rule(node_addr, temp_c) + + self.rules.rulesUpdated.emit() + return True, "" + except Exception as e: + return False, "{0}".format(e) + + return False, QC.translate("firewall", "profile not applied") + + def delete_profile(self, node_addr, json_profile): + try: + holder = ui_pb2.FwChain() + profile = json_format.Parse(json_profile, holder) + + fwcfg = self._nodes.get_node(node_addr)['firewall'] + for sdx, n in enumerate(fwcfg.SystemRules): + for cdx, c in enumerate(n.Chains): + if c.Hook.lower() == profile.Hook and \ + c.Type.lower() == profile.Type and \ + c.Family.lower() == profile.Family and \ + c.Table.lower() == profile.Table: + + if profile.Policy == ProfileDropInput.value: + profile.Policy = ProfileAcceptInput.value + + del_candidates = [] + for rdx, r in enumerate(c.Rules): + for pr in profile.Rules: + if r.UUID == pr.UUID: + # we cannot delete the rule here, otherwise + # we'd modify the items of the loop. + del_candidates.append(rdx) + if len(del_candidates) > 0: + for rdx in del_candidates: + if rdx == len(c.Rules): # last rule + rdx = rdx - 1 + self.delete_rule(node_addr, c.Rules[rdx].UUID) + + except Exception as e: + return False, "{0}".format(e) + return False, QC.translate("firewall", "profile not deleted") diff --git a/ui/opensnitch/firewall/chains.py b/ui/opensnitch/firewall/chains.py new file mode 100644 index 0000000..d7606c3 --- /dev/null +++ b/ui/opensnitch/firewall/chains.py @@ -0,0 +1,258 @@ +import opensnitch.proto as proto +ui_pb2, ui_pb2_grpc = proto.import_() + +from .enums import * + +class Chains(): + + def __init__(self, nodes): + self._nodes = nodes + + def get(self): + chains = {} + for node in self._nodes.get_nodes(): + chains[node] = self.get_node_chains(node) + return chains + + def get_node_chains(self, addr): + node = self._nodes.get_node(addr) + if node == None: + return rules + if not 'firewall' in node: + return rules + + chains = [] + for c in node['firewall'].SystemRules: + # Chains node does not exist on <= v1.5.x + try: + chains.append(c.Chains) + except Exception: + pass + return chains + + def get_node_chains(self, addr): + node = self._nodes.get_node(addr) + if node == None: + return rules + if not 'firewall' in node: + return rules + + chains = [] + for c in node['firewall'].SystemRules: + # Chains node does not exist on <= v1.5.x + try: + chains.append(c.Chains) + except Exception: + pass + return chains + + + + def get_policy(self, node_addr=None, hook=Hooks.INPUT.value, _type=ChainType.FILTER.value, family=Family.INET.value): + fwcfg = self._nodes.get_node(node_addr)['firewall'] + for sdx, n in enumerate(fwcfg.SystemRules): + for cdx, c in enumerate(n.Chains): + if c.Hook.lower() == hook and c.Type.lower() == _type and c.Family.lower() == family: + return c.Policy + + return None + + def set_policy(self, node_addr, hook=Hooks.INPUT.value, _type=ChainType.FILTER.value, family=Family.INET.value, policy=Policy.DROP): + fwcfg = self._nodes.get_node(node_addr)['firewall'] + for sdx, n in enumerate(fwcfg.SystemRules): + for cdx, c in enumerate(n.Chains): + # XXX: support only "inet" family (ipv4/ipv6)? or allow to + # specify ipv4 OR/AND ipv6? some systems have ipv6 disabled + if c.Hook.lower() == hook and c.Type.lower() == _type and c.Family.lower() == family: + fwcfg.SystemRules[sdx].Chains[cdx].Policy = policy + + if wantedHook == Fw.Hooks.INPUT.value and wantedPolicy == Fw.Policy.DROP.value: + fwcfg.SystemRules[sdx].Chains[cdx].Rules.extend([rule.Rules[0]]) + self._nodes.add_fw_config(node_addr, fwcfg) + return True + return False + + + @staticmethod + def new( + name="", + table=Table.FILTER.value, + family=Family.INET.value, + ctype="", + hook=Hooks.INPUT.value + ): + chain = ui_pb2.FwChain() + chain.Name = name + chain.Table = table + chain.Family = family + chain.Type = ctype + chain.Hook = hook + + return chain + +# man nft +# Table 6. Standard priority names, family and hook compatibility matrix +# Name │ Value │ Families │ Hooks +# raw │ -300 │ ip, ip6, inet │ all +# mangle │ -150 │ ip, ip6, inet │ all +# dstnat │ -100 │ ip, ip6, inet │ prerouting +# filter │ 0 │ ip, ip6, inet, arp, netdev │ all +# security │ 50 │ ip, ip6, inet │ all +# srcnat │ 100 │ ip, ip6, inet │ postrouting +# + +class ChainFilter(Chains): + """ + ChainFilter returns a new chain of type filter. + + The name of the chain is the one listed with: nft list table inet filter. + It corresponds with the hook name, but can be a random name. + """ + + @staticmethod + def input(family=Family.INET.value): + chain = ui_pb2.FwChain() + chain.Name = Hooks.INPUT.value + chain.Table = Table.FILTER.value + chain.Family = family + chain.Type = ChainType.FILTER.value + chain.Hook = Hooks.INPUT.value + + return chain + + @staticmethod + def output(family=Family.INET.value): + chain = ui_pb2.FwChain() + chain.Name = Hooks.OUTPUT.value + chain.Table = Table.FILTER.value + chain.Family = family + chain.Type = ChainType.FILTER.value + chain.Hook = Hooks.OUTPUT.value + + return chain + + @staticmethod + def forward(family=Family.INET.value): + chain = ui_pb2.FwChain() + chain.Name = Hooks.FORWARD.value + chain.Table = Table.FILTER.value + chain.Family = family + chain.Type = ChainType.FILTER.value + chain.Hook = Hooks.FORWARD.value + + return chain + + + +class ChainMangle(Chains): + """ + ChainMangle returns a new chain of type mangle. + + The name of the chain is the one listed with: nft list table inet mangle. + It corresponds with the hook name, but can be a random name. + """ + + @staticmethod + def output(family=Family.INET.value): + chain = ui_pb2.FwChain() + chain.Name = Hooks.OUTPUT.value + chain.Table = Table.MANGLE.value + + chain.Family = family + chain.Type = ChainType.MANGLE.value + chain.Hook = Hooks.OUTPUT.value + + return chain + + @staticmethod + def input(family=Family.INET.value): + chain = ui_pb2.FwChain(family=Family.INET.value) + chain.Name = Hooks.INPUT.value + chain.Table = Table.MANGLE.value + + chain.Family = family + chain.Type = ChainType.MANGLE.value + chain.Hook = Hooks.INPUT.value + + return chain + + @staticmethod + def forward(family=Family.INET.value): + chain = ui_pb2.FwChain() + chain.Name = Hooks.FORWARD.value + chain.Table = Table.MANGLE.value + + chain.Family = family + chain.Type = ChainType.MANGLE.value + chain.Hook = Hooks.FORWARD.value + + return chain + + + @staticmethod + def prerouting(family=Family.INET.value): + chain = ui_pb2.FwChain() + chain.Name = Hooks.PREROUTING.value + chain.Table = Table.MANGLE.value + + chain.Family = family + chain.Type = ChainType.MANGLE.value + chain.Hook = Hooks.PREROUTING.value + + return chain + + @staticmethod + def postrouting(family=Family.INET.value): + chain = ui_pb2.FwChain() + chain.Name = Hooks.POSTROUTING.value + chain.Table = Table.MANGLE.value + + chain.Family = family + chain.Type = ChainType.MANGLE.value + chain.Hook = Hooks.POSTROUTING.value + + return chain + +class ChainDstNAT(Chains): + """ + ChainDstNAT returns a new chain of type dstnat. + + The name of the chain is the one listed with: nft list table inet nat. + It corresponds with the hook name, but can be a random name. + """ + + @staticmethod + def prerouting(family=Family.INET.value): + chain = ui_pb2.FwChain() + chain.Name = Hooks.PREROUTING.value + chain.Table = Table.NAT.value + + chain.Family = family + chain.Type = ChainType.DNAT.value + chain.Hook = Hooks.PREROUTING.value + + return chain + + @staticmethod + def output(family=Family.INET.value): + chain = ui_pb2.FwChain() + chain.Name = Hooks.OUTPUT.value + chain.Table = Table.NAT.value + + chain.Family = family + chain.Type = ChainType.DNAT.value + chain.Hook = Hooks.OUTPUT.value + + return chain + + @staticmethod + def postrouting(family=Family.INET.value): + chain = ui_pb2.FwChain() + chain.Name = Hooks.POSTROUTING.value + chain.Table = Table.NAT.value + + chain.Family = family + chain.Type = ChainType.SNAT.value + chain.Hook = Hooks.POSTROUTING.value + + return chain diff --git a/ui/opensnitch/firewall/enums.py b/ui/opensnitch/firewall/enums.py new file mode 100644 index 0000000..82996df --- /dev/null +++ b/ui/opensnitch/firewall/enums.py @@ -0,0 +1,127 @@ +from opensnitch.utils import Enums +from opensnitch.config import Config + +class Verdicts(Enums): + EMPTY = "" + ACCEPT = Config.ACTION_ACCEPT + DROP = Config.ACTION_DROP + REJECT = Config.ACTION_REJECT + RETURN = Config.ACTION_RETURN + QUEUE = Config.ACTION_QUEUE + DNAT = Config.ACTION_DNAT + SNAT = Config.ACTION_SNAT + REDIRECT = Config.ACTION_REDIRECT + TPROXY = Config.ACTION_TPROXY + #MASQUERADE = Config.ACTION_MASQUERADE + #LOG = Config.ACTION_LOG + STOP = Config.ACTION_STOP + + + +class Policy(Enums): + ACCEPT = "accept" + DROP = "drop" + +class Table(Enums): + FILTER = "filter" + MANGLE = "mangle" + NAT = "nat" + +class Hooks(Enums): + INPUT ="input" + OUTPUT ="output" + FORWARD = "forward" + PREROUTING = "prerouting" + POSTROUTING = "postrouting" + +class PortProtocols(Enums): + TCPUDP = "tcp,udp" + TCP = "tcp" + UDP = "udp" + UDPLITE = "udplite" + SCTP = "sctp" + DCCP = "dccp" + +class Protocols(Enums): + TCP = "tcp" + UDP = "udp" + UDPLITE = "udplite" + SCTP = "sctp" + DCCP = "dccp" + ICMP = "icmp" + ICMPv6 = "icmpv6" + AH = "ah" + ETHERNET = "ethernet" + GREP = "gre" + IP = "ip" + IPIP = "ipip" + L2TP = "l2tp" + COMP = "comp" + IGMP = "igmp" + ESP = "esp" + RAW = "raw" + ENCAP = "encap" + +class Family(Enums): + INET = "inet" + IPv4 = "ip" + IPv6 = "ip6" + +class ChainType(Enums): + FILTER = "filter" + MANGLE = "mangle" + ROUTE = "route" + SNAT = "natsource" + DNAT = "natdest" + +class Operator(Enums): + EQUAL = "==" + NOT_EQUAL = "!=" + GT_THAN = ">=" + GT = ">" + LT_THAN = "<=" + LT = "<" + +class TimeUnits(Enums): + SECOND = "second" + MINUTE = "minute" + HOUR = "hour" + DAY = "day" + +class RateUnits(Enums): + BYTES = "bytes" + KBYTES = "kbytes" + MBYTES = "mbytes" + GBYTES = "gbytes" + +class Statements(Enums): + """Enum of known (allowed) statements: + [tcp,udp,ip] ... + """ + # we may need in the future: + # ANY = tcp,udp,udplite,sctp,dccp + TCPUDP = "tcp,udp" + TCP = "tcp" + UDP = "udp" + UDPLITE = "udplite" + SCTP = "sctp" + DCCP = "dccp" + ICMP = "icmp" + ICMPv6 = "icmpv6" + + SPORT = "sport" + DPORT = "dport" + DADDR = "daddr" + SADDR = "saddr" + + IP = "ip" + IP6 = "ip6" + IIFNAME = "iifname" + OIFNAME = "oifname" + CT = "ct" + META = "meta" + COUNTER = "counter" + NAME = "name" + LOG = "log" + QUOTA = "quota" + LIMIT = "limit" diff --git a/ui/opensnitch/firewall/exprs.py b/ui/opensnitch/firewall/exprs.py new file mode 100644 index 0000000..c192468 --- /dev/null +++ b/ui/opensnitch/firewall/exprs.py @@ -0,0 +1,123 @@ + +import opensnitch.proto as proto +ui_pb2, ui_pb2_grpc = proto.import_() + +from .enums import * + +class Expr(): + """ + Expr returns a new nftables expression that defines a match or an action: + tcp dport 22, udp sport 53 + log prefix "xxx" + + Attributes: + op (string): operator (==, !=, ...). + what (string): name of the statement (tcp, udp, ip, ...) + value (tuple): array of values (dport -> 22, etc). + """ + @staticmethod + def new(op, what, values): + expr = ui_pb2.Expressions() + expr.Statement.Op = op + expr.Statement.Name = what + + for val in values: + exprValues = ui_pb2.StatementValues() + exprValues.Key = val[0] + exprValues.Value = val[1] + expr.Statement.Values.extend([exprValues]) + + return expr + +class ExprCt(Enums): + STATE = "state" + NEW = "new" + ESTABLISHED = "established" + RELATED = "related" + INVALID = "invalid" + SET = "set" + MARK = "mark" + +class ExprMeta(Enums): + SET = "set" + MARK = "mark" + L4PROTO = "l4proto" + SKUID = "skuid" + SKGID = "skgid" + PROTOCOL = "protocol" + PRIORITY = "priority" + +class ExprIface(Enums): + IIFNAME = "iifname" + OIFNAME = "oifname" + +class ExprICMP(Enums): + ECHO_REQUEST = "echo-request" + ECHO_REPLY = "echo-reply" + SOURCE_QUENCH = "source-quench" + DEST_UNREACHABLE = "destination-unreachable" + ROUTER_ADVERTISEMENT = "router-advertisement" + ROUTER_SOLICITATION = "router-solicitation" + REDIRECT = "redirect" + TIME_EXCEEDED = "time-exceeded" + INFO_REQUEST = "info-request" + INFO_REPLY = "info-reply" + PARAMETER_PROBLEM = "parameter-problem" + TIMESTAMP_REQUEST = "timestamp-request" + TIMESTAMP_REPLY = "timestamp-reply" + ADDRESS_MASK_REQUEST = "address-mask-request" + ADDRESS_MASK_REPLY = "address-mask-reply" + + # IPv6 + PACKET_TOO_BIG = "packet-too-big" + NEIGHBOUR_SOLICITATION = "neighbour-solicitation" + NEIGHBOUR_ADVERTISEMENT = "neighbour-advertisement" + +class ExprICMPRejectCodes(Enums): + NO_ROUTE = "no-route" + PROT_UNREACHABLE = "prot-unreachable" + PORT_UNREACHABLE = "port-unreachable" + NET_UNREACHABLE = "net-unreachable" + ADDR_UNREACHABLE = "addr-unreachable" + HOST_UNREACHABLE = "host-unreachable" + NET_PROHIBITED = "net-prohibited" + HOST_PROHIBITED = "host-prohibited" + ADMIN_PROHIBITED = "admin-prohibited" + REJECT_ROUTE = "reject-route" + REJECT_POLICY_FAIL = "policy-fail" + +class ExprLog(Enums): + LOG = "log" + LEVEL = "level" + PREFIX = "prefix" + +class ExprLogLevels(Enums): + EMERG = "emerg" + ALERT = "alert" + CRIT = "crit" + ERR = "err" + WARN = "warn" + NOTICE = "notice" + INFO = "info" + DEBUG = "debug" + AUDIT = "audit" + +class ExprCounter(Enums): + COUNTER = "counter" + PACKETS = "packets" + BYTES = "bytes" + NAME = "name" + +class ExprLimit(Enums): + OVER = "over" + LIMIT = "limit" + UNITS = "units" + RATE_UNITS = "rate-units" + TIME_UNITS = "time-units" + +class ExprQuota(Enums): + QUOTA = "quota" + OVER = "over" + UNTIL = "until" + USED = "used" + UNIT = "unit" diff --git a/ui/opensnitch/firewall/profiles.py b/ui/opensnitch/firewall/profiles.py new file mode 100644 index 0000000..6d580c3 --- /dev/null +++ b/ui/opensnitch/firewall/profiles.py @@ -0,0 +1,157 @@ + +import glob +import json +import os.path + + +class Profiles(): + + @staticmethod + def load_predefined_profiles(): + profiles = glob.glob("/etc/opensnitchd/system-fw.d/profiles/*.profile") + p = [] + for pr_path in profiles: + with open(pr_path) as f: + p.append({os.path.basename(pr_path): json.load(f)}) + + return p + + +class ProfileAcceptOutput(): + value = { + "Name": "accept-mangle-output", + "Table": "mangle", + "Family": "inet", + "Priority": "", + "Type": "mangle", + "Hook": "output", + "Policy": "accept", + "Rules": [ + ] + } + + +class ProfileDropOutput(): + value = { + "Name": "drop-mangle-output", + "Table": "mangle", + "Family": "inet", + "Priority": "", + "Type": "mangle", + "Hook": "output", + "Policy": "drop", + "Rules": [ + ] + } + + +class ProfileAcceptForward(): + value = { + "Name": "accept-mangle-forward", + "Table": "mangle", + "Family": "inet", + "Priority": "", + "Type": "mangle", + "Hook": "forward", + "Policy": "accept", + "Rules": [ + ] + } + + +class ProfileDropForward(): + value = { + "Name": "drop-mangle-forward", + "Table": "mangle", + "Family": "inet", + "Priority": "", + "Type": "mangle", + "Hook": "forward", + "Policy": "drop", + "Rules": [ + ] + } + + +class ProfileAcceptInput(): + value = { + "Name": "accept-filter-input", + "Table": "filter", + "Family": "inet", + "Priority": "", + "Type": "filter", + "Hook": "input", + "Policy": "accept", + "Rules": [ + ] + } + + +class ProfileDropInput(): + """ + Set input filter table policy to DROP and add the needed rules to allow + outbound connections. + """ + + # TODO: delete dropInput profile's rules + value = { + "Name": "drop-filter-input", + "Table": "filter", + "Family": "inet", + "Priority": "", + "Type": "filter", + "Hook": "input", + "Policy": "drop", + "Rules": [ + { + "Table": "", + "Chain": "", + "UUID": "profile-drop-inbound-2d7e6fe4-c21d-11ec-99a6-3c970e298b0c", + "Enabled": True, + "Position": "0", + "Description": "[profile-drop-inbound] allow localhost connections", + "Parameters": "", + "Expressions": [ + { + "Statement": { + "Op": "", + "Name": "iifname", + "Values": [ + { + "Key": "lo", + "Value": "" + } + ] + } + } + ], + "Target": "accept", + "TargetParameters": "" + }, + { + "Enabled": True, + "Description": "[profile-drop-inbound] allow established,related connections", + "UUID": "profile-drop-inbound-e1fc1a1c-c21c-11ec-9a2a-3c970e298b0c", + "Expressions": [ + { + "Statement": { + "Op": "", + "Name": "ct", + "Values": [ + { + "Key": "state", + "Value": "related" + }, + { + "Key": "state", + "Value": "established" + } + ] + } + } + ], + "Target": "accept", + "TargetParameters": "" + } + ] + } diff --git a/ui/opensnitch/firewall/rules.py b/ui/opensnitch/firewall/rules.py new file mode 100644 index 0000000..0ec35fd --- /dev/null +++ b/ui/opensnitch/firewall/rules.py @@ -0,0 +1,319 @@ +from PyQt5.QtCore import QObject, pyqtSignal +from PyQt5.QtCore import QCoreApplication as QC +from google.protobuf.json_format import MessageToJson +import uuid + +import opensnitch.proto as proto +ui_pb2, ui_pb2_grpc = proto.import_() + +from .enums import Operator +from .exprs import ExprLog + +class Rules(QObject): + rulesUpdated = pyqtSignal() + + # Fields defined in the protobuf, to be used as constants on other parts. + FIELD_UUID = "UUID" + FIELD_ENABLED = "Enabled" + FIELD_TARGET = "Target" + + def __init__(self, nodes): + QObject.__init__(self) + self._nodes = nodes + self.rulesUpdated.connect(self._cb_rules_updated) + + def _cb_rules_updated(self): + pass + + def add(self, addr, rule): + """Add a new rule to the corresponding table on the given node + """ + node = self._nodes.get_node(addr) + if node == None or not 'firewall' in node: + return False, QC.translate("firewall", "rule not found by its ID.") + if self.is_duplicated(addr, rule): + return False, QC.translate("firewall", "duplicated.") + + for sdx, n in enumerate(node['firewall'].SystemRules): + for cdx, c in enumerate(n.Chains): + if c.Name == rule.Name and \ + c.Hook == rule.Hook and \ + c.Table == rule.Table and \ + c.Family == rule.Family and \ + c.Type == rule.Type: + node['firewall'].SystemRules[sdx].Chains[cdx].Rules.extend([rule.Rules[0]]) + node['fwrules'][rule.Rules[0].UUID] = rule + self._nodes.add_fw_config(addr, node['firewall']) + self._nodes.add_fw_rules(addr, node['fwrules']) + + self.rulesUpdated.emit() + return True + + return False, QC.translate("firewall", "firewall table/chain not properly configured.") + + def insert(self, addr, rule, position=0): + """Insert a new rule to the corresponding table on the given node + """ + node = self._nodes.get_node(addr) + if node == None or not 'firewall' in node: + return False, QC.translate("firewall", "this node doesn't have a firewall configuration, review it.") + if self.is_duplicated(addr, rule): + return False, QC.translate("firewall", "duplicated") + + for sdx, n in enumerate(node['firewall'].SystemRules): + for cdx, c in enumerate(n.Chains): + if c.Name == rule.Name and \ + c.Hook == rule.Hook and \ + c.Table == rule.Table and \ + c.Family == rule.Family and \ + c.Type == rule.Type: + if hasattr(node['firewall'].SystemRules[sdx].Chains[cdx].Rules, "insert"): + node['firewall'].SystemRules[sdx].Chains[cdx].Rules.insert(int(position), rule.Rules[0]) + else: + node['firewall'].SystemRules[sdx].Chains[cdx].Rules.extend([rule.Rules[0]]) + node['fwrules'][rule.Rules[0].UUID] = rule + self._nodes.add_fw_config(addr, node['firewall']) + self._nodes.add_fw_rules(addr, node['fwrules']) + + self.rulesUpdated.emit() + return True, "" + + return False, QC.translate("firewall", "firewall table/chain not properly configured.") + + + def update(self, addr, uuid, rule): + node = self._nodes.get_node(addr) + if node == None or not 'firewall' in node: + return False, QC.translate("firewall", "this node doesn't have a firewall configuration, review it.") + for sdx, n in enumerate(node['firewall'].SystemRules): + for cdx, c in enumerate(n.Chains): + for rdx, r in enumerate(c.Rules): + if r.UUID == uuid: + c.Rules[rdx].CopyFrom(rule.Rules[0]) + node['firewall'].SystemRules[sdx].Chains[cdx].Rules[rdx].CopyFrom(rule.Rules[0]) + self._nodes.add_fw_config(addr, node['firewall']) + node['fwrules'][uuid] = rule + self._nodes.add_fw_rules(addr, node['fwrules']) + + self.rulesUpdated.emit() + return True, "" + + return False, QC.translate("firewall", "rule not found by its ID.") + + def get(self): + rules = [] + for node in self._nodes.get_nodes(): + node_rules = self.get_by_node(node) + rules += node_rules + + return rules + + def delete(self, addr, uuid): + node = self._nodes.get_node(addr) + if node == None or not 'firewall' in node: + return False, None + for sdx, n in enumerate(node['firewall'].SystemRules): + for cdx, c in enumerate(n.Chains): + for idx, r in enumerate(c.Rules): + if r.UUID == uuid: + del node['firewall'].SystemRules[sdx].Chains[cdx].Rules[idx] + self._nodes.add_fw_config(addr, node['firewall']) + if uuid in node['fwrules']: + del node['fwrules'][uuid] + self._nodes.add_fw_rules(addr, node['fwrules']) + else: + # raise Error("rules doesn't have UUID field") + return False, None + + self.rulesUpdated.emit() + return True, node['firewall'] + + return False, None + + def get_by_node(self, addr): + rules = [] + node = self._nodes.get_node(addr) + if node == None: + return rules + if not 'firewall' in node: + return rules + for u in node['firewall'].SystemRules: + for c in u.Chains: + for r in c.Rules: + rules.append(Rules.to_array(addr, c, r)) + return rules + + def get_by_uuid(self, addr, uuid): + rules = [] + node = self._nodes.get_node(addr) + if node == None: + return rules + if not 'firewall' in node: + return rules + for u in node['firewall'].SystemRules: + for c in u.Chains: + for r in c.Rules: + if r.UUID == uuid: + return r + return None + + def swap(self, view, addr, uuid, old_pos, new_pos): + """ + swap changes the order of 2 rows. + + The list of rules is ordered from top to bottom: 0,1,2,3... + so a click on the down button sums +1, a click on the up button rest -1 + """ + node = self._nodes.get_node(addr) + if node == None: + return + if not 'firewall' in node: + return + for sdx, c in enumerate(node['firewall'].SystemRules): + for cdx, u in enumerate(c.Chains): + nrules = len(u.Rules) + for rdx, r in enumerate(u.Rules): + # is the last rule + if new_pos > nrules and new_pos < nrules: + break + if u.Rules[rdx].UUID == uuid: + old_rule = u.Rules[old_pos] + new_rule = ui_pb2.FwRule() + new_rule.CopyFrom(u.Rules[new_pos]) + + node['firewall'].SystemRules[sdx].Chains[cdx].Rules[new_pos].CopyFrom(old_rule) + node['firewall'].SystemRules[sdx].Chains[cdx].Rules[old_pos].CopyFrom(new_rule) + + self._nodes.add_fw_config(addr, node['firewall']) + #self._nodes.add_fw_rules(addr, node['fwrules']) + + self.rulesUpdated.emit() + return True + return False + + def is_duplicated(self, addr, orig_rule): + # we need to duplicate the rule, otherwise we'd modify the UUID of the + # orig rule. + temp_c = ui_pb2.FwChain() + temp_c.CopyFrom(orig_rule) + # the UUID will be different, so zero it out. + # but keep a copy of the original one. + orig_uuid = temp_c.Rules[0].UUID + temp_c.Rules[0].UUID = "" + node = self._nodes.get_node(addr) + if node == None: + return False + if not 'firewall' in node: + return False + for n in node['firewall'].SystemRules: + for c in n.Chains: + if c.Name == temp_c.Name and \ + c.Hook == temp_c.Hook and \ + c.Table == temp_c.Table and \ + c.Family == temp_c.Family and \ + c.Type == temp_c.Type: + for rdx, r in enumerate(c.Rules): + uuid = c.Rules[rdx].UUID + c.Rules[rdx].UUID = "" + is_equal = (c.Rules[rdx].SerializeToString() == temp_c.Rules[0].SerializeToString() or orig_uuid == uuid) + c.Rules[rdx].UUID = uuid + + if is_equal: + return True + + return False + + @staticmethod + def new( + enabled=True, + _uuid="", + description="", + expressions=None, + target="", + target_parms="" + ): + rule = ui_pb2.FwRule() + if _uuid == "": + rule.UUID = str(uuid.uuid1()) + else: + rule.UUID = _uuid + rule.Enabled = enabled + rule.Description = description + if expressions != None: + rule.Expressions.extend([expressions]) + rule.Target = target + rule.TargetParameters = target_parms + + return rule + + @staticmethod + def new_flat(c, r): + """Create a new "flat" rule from a hierarchical one. + Transform from: + { + xx: + { + yy: { + to: + {xx:, yy} + """ + + chain = ui_pb2.FwChain() + chain.CopyFrom(c) + del chain.Rules[:] + chain.Rules.extend([r]) + + return chain + + @staticmethod + def to_dict(sysRules): + """Transform json/protobuf struct to flat structure. + This is the default format used to find rules in the table view. + """ + rules={} + for s in sysRules: + for c in s.Chains: + if len(c.Rules) == 0: + continue + for r in c.Rules: + rules[r.UUID] = Rules.new_flat(c, r) + + return rules + + @staticmethod + def to_json(rule): + try: + return MessageToJson(rule) + except: + return None + + @staticmethod + def to_array(addr, chain, rule): + cols = [] + cols.append(rule.UUID) + cols.append(addr) + cols.append(chain.Name) + cols.append(chain.Table) + cols.append(chain.Family) + cols.append(chain.Hook) + cols.append(str(rule.Enabled)) + cols.append(rule.Description) + exprs = "" + for e in rule.Expressions: + exprs += "{0} {1}".format( + e.Statement.Name, + "".join( + [ + "{0} {1}{2} ".format( + h.Key, + e.Statement.Op + " " if e.Statement.Op != Operator.EQUAL.value else "", + "\"{0}\"".format(h.Value) if h.Key == ExprLog.PREFIX.value else h.Value + ) for h in e.Statement.Values + ] + ) + ) + cols.append(exprs) + cols.append(rule.Target) + cols.append(rule.TargetParameters) + + return cols diff --git a/ui/opensnitch/firewall/utils.py b/ui/opensnitch/firewall/utils.py new file mode 100644 index 0000000..3e6882a --- /dev/null +++ b/ui/opensnitch/firewall/utils.py @@ -0,0 +1,24 @@ + +from google.protobuf import __version__ as protobuf_version +from .enums import * + +class Utils(): + + @staticmethod + def isExprPort(value): + """Return true if the value is valid for a port based rule: + nft add rule ... tcp dport 22 accept + """ + return value == Statements.TCP.value or \ + value == Statements.UDP.value or \ + value == Statements.UDPLITE.value or \ + value == Statements.SCTP.value or \ + value == Statements.DCCP.value + + @staticmethod + def isProtobufSupported(): + """ + The protobuf operations append() and insert() were introduced on 3.8.0 version. + """ + vparts = protobuf_version.split(".") + return int(vparts[0]) >= 3 and int(vparts[1]) >= 8 diff --git a/ui/opensnitch/nodes.py b/ui/opensnitch/nodes.py new file mode 100644 index 0000000..af52638 --- /dev/null +++ b/ui/opensnitch/nodes.py @@ -0,0 +1,397 @@ +from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot +from queue import Queue +from datetime import datetime +import time +import json + +from opensnitch.database import Database +from opensnitch.config import Config +from opensnitch.utils import NetworkInterfaces +from opensnitch.rules import Rules + +import opensnitch.proto as proto +ui_pb2, ui_pb2_grpc = proto.import_() + +class Nodes(QObject): + __instance = None + nodesUpdated = pyqtSignal(int) # total + + LOG_TAG = "[Nodes]: " + ONLINE = "\u2713 online" + OFFLINE = "\u2613 offline" + WARNING = "\u26a0" + + @staticmethod + def instance(): + if Nodes.__instance == None: + Nodes.__instance = Nodes() + return Nodes.__instance + + def __init__(self): + QObject.__init__(self) + self._db = Database.instance() + self._rules = Rules() + self._nodes = {} + self._notifications_sent = {} + self._interfaces = NetworkInterfaces() + + def count(self): + return len(self._nodes) + + def add(self, _peer, client_config=None): + try: + proto, addr = self.get_addr(_peer) + peer = proto+":"+addr + if peer not in self._nodes: + self._nodes[peer] = { + 'notifications': Queue(), + 'online': True, + 'last_seen': datetime.now() + } + else: + self._nodes[peer]['last_seen'] = datetime.now() + + self._nodes[peer]['online'] = True + self.add_data(peer, client_config) + self.update(peer) + + self.nodesUpdated.emit(self.count()) + + return self._nodes[peer], peer + + except Exception as e: + print(self.LOG_TAG, "exception adding/updating node: ", e, "addr:", addr, "config:", client_config) + + return None, None + + def add_data(self, addr, client_config): + if client_config != None: + self._nodes[addr]['data'] = self.get_client_config(client_config) + self.add_fw_config(addr, client_config.systemFirewall) + self._rules.add_rules(addr, client_config.rules) + + def add_fw_config(self, addr, fwconfig): + self._nodes[addr]['firewall'] = fwconfig + + def add_fw_rules(self, addr, fwconfig): + self._nodes[addr]['fwrules'] = fwconfig + + def add_rule(self, time, node, name, description, enabled, precedence, nolog, action, duration, op_type, op_sensitive, op_operand, op_data, created): + # don't add rule if the user has selected to exclude temporary + # rules + if duration in Config.RULES_DURATION_FILTER: + return + + self._rules.add(time, node, name, description, enabled, precedence, nolog, action, duration, op_type, op_sensitive, op_operand, op_data, created) + + def add_rules(self, addr, rules): + try: + self._rules.add_rules(addr, rules) + except Exception as e: + print(self.LOG_TAG + " exception adding node to db: ", e) + + def delete_rule(self, rule_name, addr, callback): + deleted_rule = self._rules.delete(rule_name, addr, callback) + if deleted_rule == None: + print(self.LOG_TAG, "error deleting rule", rule_name) + return None, None + + noti = ui_pb2.Notification(type=ui_pb2.DELETE_RULE, rules=[deleted_rule]) + if addr != None: + nid = self.send_notification(addr, noti, callback) + else: + nid = self.send_notifications(noti, callback) + + return nid, noti + + def delete_rule_by_field(self, field, values): + return self._rules.delete_by_field(field, values) + + def rule_to_json(self, addr, rule_name): + return self._rules.rule_to_json(addr, rule_name) + + def export_rule(self, addr, rule_name, outdir): + return self._rules.export_rule(addr, rule_name, outdir) + + def export_rules(self, addr, outdir): + return self._rules.export_rules(addr, outdir) + + def import_rules(self, addr=None, rulesdir="", callback=None): + rules_list = self._rules.import_rules(rulesdir) + if rules_list == None: + return None, None, None + + notif = ui_pb2.Notification( + id=int(str(time.time()).replace(".", "")), + type=ui_pb2.CHANGE_RULE, + data="", + rules=rules_list) + + if addr != None: + nid = self.send_notification(addr, notif, callback) + else: + nid = self.send_notifications(notif, callback) + + return nid, notif, rules_list + + def update_rule_time(self, time, rule_name, addr): + self._rules.update_time(time, rule_name, addr) + + def delete_all(self): + self.send_notifications(None) + self._nodes = {} + self.nodesUpdated.emit(self.count()) + + def delete(self, peer): + try: + proto, addr = self.get_addr(peer) + addr = "%s:%s" % (proto, addr) + # Force the node to get one new item from queue, + # in order to loop and exit. + self._nodes[addr]['notifications'].put(None) + except: + addr = peer + + if addr in self._nodes: + del self._nodes[addr] + self.nodesUpdated.emit(self.count()) + + def get(self): + return self._nodes + + def get_node(self, addr): + try: + return self._nodes[addr] + except Exception as e: + return None + + def get_nodes(self): + return self._nodes + + def get_node_config(self, addr): + try: + if addr not in self._nodes: + return None + return self._nodes[addr]['data'].config + except Exception as e: + print(self.LOG_TAG + " exception get_node_config(): ", e) + return None + + def get_client_config(self, client_config): + try: + node_config = json.loads(client_config.config) + if 'LogLevel' not in node_config: + node_config['LogLevel'] = 1 + client_config.config = json.dumps(node_config) + except Exception as e: + print(self.LOG_TAG, "exception parsing client config", e) + + return client_config + + def get_addr(self, peer): + try: + peer = peer.split(":") + # WA for backward compatibility + if peer[0] == "unix" and peer[1] == "": + peer[1] = "/local" + return peer[0], peer[1] + except: + print(self.LOG_TAG, "get_addr() error getting addr:", peer) + return peer + + def is_local(self, addr): + if addr.startswith("unix"): + return True + + if addr.startswith("ipv4") or addr.startswith("ipv6"): + ifaces = self._interfaces.list() + for name in ifaces: + if ifaces[name] in addr: + return True + + return False + + def get_notifications(self): + notlist = [] + try: + for c in self._nodes: + if self._nodes[c]['online'] == False: + continue + if self._nodes[c]['notifications'].empty(): + continue + notif = self._nodes[c]['notifications'].get(False) + if notif != None: + self._nodes[c]['notifications'].task_done() + notlist.append(notif) + except Exception as e: + print(self.LOG_TAG + " exception get_notifications(): ", e) + + return notlist + + def save_node_config(self, addr, config): + try: + self._nodes[addr]['data'].config = config + except Exception as e: + print(self.LOG_TAG + " exception saving node config: ", e, addr, config) + + def save_nodes_config(self, config): + try: + for c in self._nodes: + self._nodes[c]['data'].config = config + except Exception as e: + print(self.LOG_TAG + " exception saving nodes config: ", e, config) + + def change_node_config(self, addr, config, _callback): + _cfg = json.dumps(config, indent=" ") + notif = ui_pb2.Notification( + id=int(str(time.time()).replace(".", "")), + type=ui_pb2.CHANGE_CONFIG, + data=_cfg, + rules=[]) + self.save_node_config(addr, _cfg) + return self.send_notification(addr, notif, _callback), notif + + def start_interception(self, _addr=None, _callback=None): + return self.firewall(not_type=ui_pb2.ENABLE_INTERCEPTION, addr=_addr, callback=_callback) + + def stop_interception(self, _addr=None, _callback=None): + return self.firewall(not_type=ui_pb2.DISABLE_INTERCEPTION, addr=_addr, callback=_callback) + + def firewall(self, not_type=ui_pb2.ENABLE_INTERCEPTION, addr=None, callback=None): + noti = ui_pb2.Notification(clientName="", serverName="", type=not_type, data="", rules=[]) + if addr == None: + nid = self.send_notifications(noti, callback) + else: + nid = self.send_notification(addr, noti, callback) + + return nid, noti + + def send_notification(self, addr, notification, callback_signal=None): + try: + notification.id = int(str(time.time()).replace(".", "")) + if addr not in self._nodes: + # FIXME: the reply is sent before we return the notification id + if callback_signal != None: + callback_signal.emit( + ui_pb2.NotificationReply( + id=notification.id, + code=ui_pb2.ERROR, + data="node not connected: {0}".format(addr) + ) + ) + return notification.id + + self._notifications_sent[notification.id] = { + 'callback': callback_signal, + 'type': notification.type + } + + self._nodes[addr]['notifications'].put(notification) + except Exception as e: + print(self.LOG_TAG + " exception sending notification: ", e, addr, notification) + if callback_signal != None: + callback_signal.emit( + ui_pb2.NotificationReply( + id=notification.id, + code=ui_pb2.ERROR, + data="Notification not sent ({0}):<br>{1}".format(addr, e) + ) + ) + + return notification.id + + def send_notifications(self, notification, callback_signal=None): + """ + Enqueues a notification to the clients queue. + It'll be retrieved and delivered by get_notifications + """ + try: + notification.id = int(str(time.time()).replace(".", "")) + for c in self._nodes: + self._nodes[c]['notifications'].put(notification) + self._notifications_sent[notification.id] = { + 'callback': callback_signal, + 'type': notification.type + } + except Exception as e: + print(self.LOG_TAG + " exception sending notifications: ", e, notification) + + return notification.id + + def reply_notification(self, addr, reply): + try: + if reply == None: + print(self.LOG_TAG, " reply notification None") + return + + if reply.id not in self._notifications_sent: + print(self.LOG_TAG, " reply notification not in the list:", reply.id) + return + + if self._notifications_sent[reply.id] == None: + print(self.LOG_TAG, " reply notification body empty:", reply.id) + return + + if self._notifications_sent[reply.id]['callback'] != None: + self._notifications_sent[reply.id]['callback'].emit(reply) + + # delete only one-time notifications + # we need the ID of streaming notifications from the server + # (monitor_process for example) to keep track of the data sent to us. + if self._notifications_sent[reply.id]['type'] != ui_pb2.MONITOR_PROCESS: + del self._notifications_sent[reply.id] + except Exception as e: + print(self.LOG_TAG, "notification exception:", e) + + def stop_notifications(self): + """Send a dummy notification to force Notifications class to exit. + """ + exit_noti = ui_pb2.Notification(clientName="", serverName="", type=0, data="", rules=[]) + self.send_notifications(exit_noti) + + def update(self, peer, status=ONLINE): + try: + proto, addr = self.get_addr(peer) + self._db.update("nodes", + "hostname=?,version=?,last_connection=?,status=?", + ( + self._nodes[proto+":"+addr]['data'].name, + self._nodes[proto+":"+addr]['data'].version, + datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + status, + "{0}:{1}".format(proto, addr)), + "addr=?" + ) + except Exception as e: + print(self.LOG_TAG + " exception updating DB: ", e, peer) + + def update_all(self, status=OFFLINE): + try: + for peer in self._nodes: + self._db.update("nodes", + "hostname=?,version=?,last_connection=?,status=?", + ( + self._nodes[peer]['data'].name, + self._nodes[peer]['data'].version, + datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + status, + peer), + "addr=?" + ) + except Exception as e: + print(self.LOG_TAG + " exception updating nodes: ", e) + + def reset_status(self): + try: + self._db.update("nodes", "status=?", (self.OFFLINE,)) + except Exception as e: + print(self.LOG_TAG + " exception resetting nodes status: ", e) + + def reload_fw(self, addr, fw_config, callback): + notif = ui_pb2.Notification( + id=int(str(time.time()).replace(".", "")), + type=ui_pb2.RELOAD_FW_RULES, + sysFirewall=fw_config + ) + nid = self.send_notification(addr, notif, callback) + return nid, notif diff --git a/ui/opensnitch/notifications.py b/ui/opensnitch/notifications.py new file mode 100644 index 0000000..461209b --- /dev/null +++ b/ui/opensnitch/notifications.py @@ -0,0 +1,132 @@ +from PyQt5.QtCore import QCoreApplication as QC +import os +from opensnitch.utils import Utils +from opensnitch.config import Config + +class DesktopNotifications(): + """DesktopNotifications display informative pop-ups using the system D-Bus. + The notifications are handled and configured by the system. + + The notification daemon also decides where to show the notifications, as well + as how to group them. + + The body of a notification supports markup (if the implementation supports it): + https://people.gnome.org/~mccann/docs/notification-spec/notification-spec-latest.html#markup + Basically: <a>, <u>, <b>, <i> and <img>. New lines can be added with the regular \n. + + It also support actions (buttons). + + https://notify2.readthedocs.io/en/latest/ + """ + + _cfg = Config.init() + + # list of hints: + # https://people.gnome.org/~mccann/docs/notification-spec/notification-spec-latest.html#hints + HINT_DESKTOP_ENTRY = "desktop-entry" + CATEGORY_NETWORK = "network" + + EXPIRES_DEFAULT = 0 + NEVER_EXPIRES = -1 + + URGENCY_LOW = 0 + URGENCY_NORMAL = 1 + URGENCY_CRITICAL = 2 + + # must be a string + ACTION_ID_OPEN = "action-open" + ACTION_ID_ALLOW = "action-allow" + ACTION_ID_DENY = "action-deny" + + def __init__(self): + self.ACTION_OPEN = QC.translate("popups", "Open") + self.ACTION_ALLOW = QC.translate("popups", "Allow") + self.ACTION_DENY = QC.translate("popups", "Deny") + self.IS_LIBNOTIFY_AVAILABLE = True + self.DOES_SUPPORT_ACTIONS = True + + try: + import notify2 + self.ntf2 = notify2 + mloop = 'glib' + + # First try to initialise the D-Bus connection with the given + # mainloop. + # If it fails, we'll try to initialise it without it. + try: + self.ntf2.init("opensnitch", mainloop=mloop) + except Exception: + self.DOES_SUPPORT_ACTIONS = False + self.ntf2.init("opensnitch") + + # usually because dbus mainloop is not initiated, specially + # with 'qt' + # FIXME: figure out how to init it, or how to connect to an + # existing session. + print("DesktopNotifications(): system doesn't support actions. Available capabilities:") + print(self.ntf2.get_server_caps()) + + + # Example: ['actions', 'action-icons', 'body', 'body-markup', 'icon-static', 'persistence', 'sound'] + if ('actions' not in self.ntf2.get_server_caps()): + self.DOES_SUPPORT_ACTIONS = False + + except Exception as e: + print("DesktopNotifications not available (install python3-notify2):", e) + self.IS_LIBNOTIFY_AVAILABLE = False + + def is_available(self): + return self.IS_LIBNOTIFY_AVAILABLE + + def are_enabled(self): + return self._cfg.getBool(Config.NOTIFICATIONS_ENABLED, True) + + def support_actions(self): + """Returns true if the notifications daemon support actions(buttons). + This depends on 2 factors: + - If the notification server actually supports it (get_server_caps()). + - If there's a dbus instance running. + """ + return self.DOES_SUPPORT_ACTIONS + + def show(self, title, body, icon="dialog-information", urgency=URGENCY_NORMAL, callback=None): + try: + ntf = self.ntf2.Notification(title, body, icon) + + ntf.set_urgency(urgency) + ntf.set_category(self.CATEGORY_NETWORK) + # used to display our app icon and name. + # Note: setting this Hint causes some DEs to call opensnitch_ui.desktop file, + # that as of today, kills and relaunches the current opensnitch-ui process. + #ntf.set_hint(self.HINT_DESKTOP_ENTRY, "opensnitch_ui") + if self.DOES_SUPPORT_ACTIONS and callback != None: + ntf.add_action(self.ACTION_ID_OPEN, self.ACTION_OPEN, callback) + ntf.show() + except Exception as e: + print("[notifications] show() exception:", e) + raise Exception("[notifications] show() exception:", e) + + # TODO: + # - construct a rule with the default configured parameters. + # - create a common dialogs/prompt.py:_send_rule(), maybe in utils.py + def ask(self, connection, timeout, callback): + c = connection + title = QC.translate("popups", "New outgoing connection") + body = c.process_path + "\n" + body = body + QC.translate("popups", "is connecting to <b>%s</b> on %s port %d") % ( \ + c.dst_host or c.dst_ip, + c.protocol.upper(), + c.dst_port ) + + ntf = self.ntf2.Notification(title, body, "dialog-warning") + timeout = self._cfg.getInt(Config.DEFAULT_TIMEOUT_KEY, 15) + ntf.set_timeout(timeout * 1000) + ntf.timeout = timeout * 1000 + if self.DOES_SUPPORT_ACTIONS: + ntf.set_urgency(self.ntf2.URGENCY_CRITICAL) + ntf.add_action(self.ACTION_ID_ALLOW, self.ACTION_ALLOW, callback, connection) + ntf.add_action(self.ACTION_ID_DENY, self.ACTION_DENY, callback, connection) + #ntf.add_action("open-gui", QC.translate("popups", "View"), callback, connection) + ntf.set_category(self.CATEGORY_NETWORK) + ntf.set_hint(self.HINT_DESKTOP_ENTRY, "opensnitch_ui") + ntf.show() diff --git a/ui/opensnitch/proto/__init__.py b/ui/opensnitch/proto/__init__.py new file mode 100644 index 0000000..c712d44 --- /dev/null +++ b/ui/opensnitch/proto/__init__.py @@ -0,0 +1,57 @@ +# Copyright (C) 2018 Simone Margaritelli +# 2019-2025 Gustavo Iñiguez Goia +# +# This file is part of OpenSnitch. +# +# OpenSnitch is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenSnitch is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenSnitch. If not, see <http://www.gnu.org/licenses/>. + +from packaging.version import Version +import importlib +from opensnitch.utils import Versions + +# Protobuffers compiled with protobuf < 3.20.0 are incompatible with +# protobuf >= 4.0.0 +# https://github.com/evilsocket/opensnitch/wiki/GUI-known-problems#gui-does-not-show-up +# +# In order to solve this issue, we provide several protobuffers: +# proto.ui_pb2* for protobuf >= 4.0.0 +# proto.pre3200.ui_pb2* for protobuf >= 3.6.0 and < 3.20.0 +# +# To avoid import errors, each protobuffer must be placed in its own directory, +# and the name of the protobuffer files must be named with the syntax +# <prefix>_pb2.py/<prefix>_pb2_grpc.py: +# ui_pb2.py and ui_pb2_grpc.py + +default_pb = "opensnitch.proto.ui_pb2" +default_grpc = "opensnitch.proto.ui_pb2_grpc" +old_pb = "opensnitch.proto.pre3200.ui_pb2" +old_grpc = "opensnitch.proto.pre3200.ui_pb2_grpc" + +def import_(): + """load the protobuffer needed based on the grpc and protobuffer version + installed in the system. + """ + try: + gui_version, grpc_version, proto_version = Versions.get() + proto_ver = default_pb + grpc_ver = default_grpc + + if Version(proto_version) < Version("3.20.0"): + proto_ver = old_pb + grpc_ver = old_grpc + + return importlib.import_module(proto_ver), importlib.import_module(grpc_ver) + except Exception as e: + print("error importing protobuffer: ", repr(e)) + return importlib.import_module(default_pb, default_grpc) diff --git a/ui/opensnitch/proto/pre3200/ui_pb2.py b/ui/opensnitch/proto/pre3200/ui_pb2.py new file mode 100644 index 0000000..f13ec65 --- /dev/null +++ b/ui/opensnitch/proto/pre3200/ui_pb2.py @@ -0,0 +1,2247 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: ui.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf.internal import enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='ui.proto', + package='protocol', + syntax='proto3', + serialized_options=_b('Z3github.com/evilsocket/opensnitch/daemon/ui/protocol'), + serialized_pb=_b('\n\x08ui.proto\x12\x08protocol\"\xcb\x04\n\x05\x41lert\x12\n\n\x02id\x18\x01 \x01(\x04\x12\"\n\x04type\x18\x02 \x01(\x0e\x32\x14.protocol.Alert.Type\x12&\n\x06\x61\x63tion\x18\x03 \x01(\x0e\x32\x16.protocol.Alert.Action\x12*\n\x08priority\x18\x04 \x01(\x0e\x32\x18.protocol.Alert.Priority\x12\"\n\x04what\x18\x05 \x01(\x0e\x32\x14.protocol.Alert.What\x12\x0e\n\x04text\x18\x06 \x01(\tH\x00\x12!\n\x04proc\x18\x08 \x01(\x0b\x32\x11.protocol.ProcessH\x00\x12$\n\x04\x63onn\x18\t \x01(\x0b\x32\x14.protocol.ConnectionH\x00\x12\x1e\n\x04rule\x18\n \x01(\x0b\x32\x0e.protocol.RuleH\x00\x12\"\n\x06\x66wrule\x18\x0b \x01(\x0b\x32\x10.protocol.FwRuleH\x00\")\n\x08Priority\x12\x07\n\x03LOW\x10\x00\x12\n\n\x06MEDIUM\x10\x01\x12\x08\n\x04HIGH\x10\x02\"(\n\x04Type\x12\t\n\x05\x45RROR\x10\x00\x12\x0b\n\x07WARNING\x10\x01\x12\x08\n\x04INFO\x10\x02\"2\n\x06\x41\x63tion\x12\x08\n\x04NONE\x10\x00\x12\x0e\n\nSHOW_ALERT\x10\x01\x12\x0e\n\nSAVE_TO_DB\x10\x02\"l\n\x04What\x12\x0b\n\x07GENERIC\x10\x00\x12\x10\n\x0cPROC_MONITOR\x10\x01\x12\x0c\n\x08\x46IREWALL\x10\x02\x12\x0e\n\nCONNECTION\x10\x03\x12\x08\n\x04RULE\x10\x04\x12\x0b\n\x07NETLINK\x10\x05\x12\x10\n\x0cKERNEL_EVENT\x10\x06\x42\x06\n\x04\x64\x61ta\"\x19\n\x0bMsgResponse\x12\n\n\x02id\x18\x01 \x01(\x04\"o\n\x05\x45vent\x12\x0c\n\x04time\x18\x01 \x01(\t\x12(\n\nconnection\x18\x02 \x01(\x0b\x32\x14.protocol.Connection\x12\x1c\n\x04rule\x18\x03 \x01(\x0b\x32\x0e.protocol.Rule\x12\x10\n\x08unixnano\x18\x04 \x01(\x03\"\xd3\x06\n\nStatistics\x12\x16\n\x0e\x64\x61\x65mon_version\x18\x01 \x01(\t\x12\r\n\x05rules\x18\x02 \x01(\x04\x12\x0e\n\x06uptime\x18\x03 \x01(\x04\x12\x15\n\rdns_responses\x18\x04 \x01(\x04\x12\x13\n\x0b\x63onnections\x18\x05 \x01(\x04\x12\x0f\n\x07ignored\x18\x06 \x01(\x04\x12\x10\n\x08\x61\x63\x63\x65pted\x18\x07 \x01(\x04\x12\x0f\n\x07\x64ropped\x18\x08 \x01(\x04\x12\x11\n\trule_hits\x18\t \x01(\x04\x12\x13\n\x0brule_misses\x18\n \x01(\x04\x12\x33\n\x08\x62y_proto\x18\x0b \x03(\x0b\x32!.protocol.Statistics.ByProtoEntry\x12\x37\n\nby_address\x18\x0c \x03(\x0b\x32#.protocol.Statistics.ByAddressEntry\x12\x31\n\x07\x62y_host\x18\r \x03(\x0b\x32 .protocol.Statistics.ByHostEntry\x12\x31\n\x07\x62y_port\x18\x0e \x03(\x0b\x32 .protocol.Statistics.ByPortEntry\x12/\n\x06\x62y_uid\x18\x0f \x03(\x0b\x32\x1f.protocol.Statistics.ByUidEntry\x12=\n\rby_executable\x18\x10 \x03(\x0b\x32&.protocol.Statistics.ByExecutableEntry\x12\x1f\n\x06\x65vents\x18\x11 \x03(\x0b\x32\x0f.protocol.Event\x1a.\n\x0c\x42yProtoEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a\x30\n\x0e\x42yAddressEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a-\n\x0b\x42yHostEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a-\n\x0b\x42yPortEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a,\n\nByUidEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a\x33\n\x11\x42yExecutableEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\">\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x04\x12#\n\x05stats\x18\x02 \x01(\x0b\x32\x14.protocol.Statistics\"\x17\n\tPingReply\x12\n\n\x02id\x18\x01 \x01(\x04\"\x89\x02\n\x07Process\x12\x0b\n\x03pid\x18\x01 \x01(\x04\x12\x0c\n\x04ppid\x18\x02 \x01(\x04\x12\x0b\n\x03uid\x18\x03 \x01(\x04\x12\x0c\n\x04\x63omm\x18\x04 \x01(\t\x12\x0c\n\x04path\x18\x05 \x01(\t\x12\x0c\n\x04\x61rgs\x18\x06 \x03(\t\x12\'\n\x03\x65nv\x18\x07 \x03(\x0b\x32\x1a.protocol.Process.EnvEntry\x12\x0b\n\x03\x63wd\x18\x08 \x01(\t\x12\x10\n\x08io_reads\x18\t \x01(\x04\x12\x11\n\tio_writes\x18\n \x01(\x04\x12\x11\n\tnet_reads\x18\x0b \x01(\x04\x12\x12\n\nnet_writes\x18\x0c \x01(\x04\x1a*\n\x08\x45nvEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xc8\x02\n\nConnection\x12\x10\n\x08protocol\x18\x01 \x01(\t\x12\x0e\n\x06src_ip\x18\x02 \x01(\t\x12\x10\n\x08src_port\x18\x03 \x01(\r\x12\x0e\n\x06\x64st_ip\x18\x04 \x01(\t\x12\x10\n\x08\x64st_host\x18\x05 \x01(\t\x12\x10\n\x08\x64st_port\x18\x06 \x01(\r\x12\x0f\n\x07user_id\x18\x07 \x01(\r\x12\x12\n\nprocess_id\x18\x08 \x01(\r\x12\x14\n\x0cprocess_path\x18\t \x01(\t\x12\x13\n\x0bprocess_cwd\x18\n \x01(\t\x12\x14\n\x0cprocess_args\x18\x0b \x03(\t\x12\x39\n\x0bprocess_env\x18\x0c \x03(\x0b\x32$.protocol.Connection.ProcessEnvEntry\x1a\x31\n\x0fProcessEnvEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"l\n\x08Operator\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0f\n\x07operand\x18\x02 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\t\x12\x11\n\tsensitive\x18\x04 \x01(\x08\x12 \n\x04list\x18\x05 \x03(\x0b\x32\x12.protocol.Operator\"\xb6\x01\n\x04Rule\x12\x0f\n\x07\x63reated\x18\x01 \x01(\x03\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\x12\x0f\n\x07\x65nabled\x18\x04 \x01(\x08\x12\x12\n\nprecedence\x18\x05 \x01(\x08\x12\r\n\x05nolog\x18\x06 \x01(\x08\x12\x0e\n\x06\x61\x63tion\x18\x07 \x01(\t\x12\x10\n\x08\x64uration\x18\x08 \x01(\t\x12$\n\x08operator\x18\t \x01(\x0b\x32\x12.protocol.Operator\"-\n\x0fStatementValues\x12\x0b\n\x03Key\x18\x01 \x01(\t\x12\r\n\x05Value\x18\x02 \x01(\t\"P\n\tStatement\x12\n\n\x02Op\x18\x01 \x01(\t\x12\x0c\n\x04Name\x18\x02 \x01(\t\x12)\n\x06Values\x18\x03 \x03(\x0b\x32\x19.protocol.StatementValues\"5\n\x0b\x45xpressions\x12&\n\tStatement\x18\x01 \x01(\x0b\x32\x13.protocol.Statement\"\xd6\x01\n\x06\x46wRule\x12\r\n\x05Table\x18\x01 \x01(\t\x12\r\n\x05\x43hain\x18\x02 \x01(\t\x12\x0c\n\x04UUID\x18\x03 \x01(\t\x12\x0f\n\x07\x45nabled\x18\x04 \x01(\x08\x12\x10\n\x08Position\x18\x05 \x01(\x04\x12\x13\n\x0b\x44\x65scription\x18\x06 \x01(\t\x12\x12\n\nParameters\x18\x07 \x01(\t\x12*\n\x0b\x45xpressions\x18\x08 \x03(\x0b\x32\x15.protocol.Expressions\x12\x0e\n\x06Target\x18\t \x01(\t\x12\x18\n\x10TargetParameters\x18\n \x01(\t\"\x95\x01\n\x07\x46wChain\x12\x0c\n\x04Name\x18\x01 \x01(\t\x12\r\n\x05Table\x18\x02 \x01(\t\x12\x0e\n\x06\x46\x61mily\x18\x03 \x01(\t\x12\x10\n\x08Priority\x18\x04 \x01(\t\x12\x0c\n\x04Type\x18\x05 \x01(\t\x12\x0c\n\x04Hook\x18\x06 \x01(\t\x12\x0e\n\x06Policy\x18\x07 \x01(\t\x12\x1f\n\x05Rules\x18\x08 \x03(\x0b\x32\x10.protocol.FwRule\"M\n\x08\x46wChains\x12\x1e\n\x04Rule\x18\x01 \x01(\x0b\x32\x10.protocol.FwRule\x12!\n\x06\x43hains\x18\x02 \x03(\x0b\x32\x11.protocol.FwChain\"X\n\x0bSysFirewall\x12\x0f\n\x07\x45nabled\x18\x01 \x01(\x08\x12\x0f\n\x07Version\x18\x02 \x01(\r\x12\'\n\x0bSystemRules\x18\x03 \x03(\x0b\x32\x12.protocol.FwChains\"\xc4\x01\n\x0c\x43lientConfig\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x19\n\x11isFirewallRunning\x18\x04 \x01(\x08\x12\x0e\n\x06\x63onfig\x18\x05 \x01(\t\x12\x10\n\x08logLevel\x18\x06 \x01(\r\x12\x1d\n\x05rules\x18\x07 \x03(\x0b\x32\x0e.protocol.Rule\x12-\n\x0esystemFirewall\x18\x08 \x01(\x0b\x32\x15.protocol.SysFirewall\"\xbb\x01\n\x0cNotification\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x12\n\nclientName\x18\x02 \x01(\t\x12\x12\n\nserverName\x18\x03 \x01(\t\x12\x1e\n\x04type\x18\x04 \x01(\x0e\x32\x10.protocol.Action\x12\x0c\n\x04\x64\x61ta\x18\x05 \x01(\t\x12\x1d\n\x05rules\x18\x06 \x03(\x0b\x32\x0e.protocol.Rule\x12*\n\x0bsysFirewall\x18\x07 \x01(\x0b\x32\x15.protocol.SysFirewall\"\\\n\x11NotificationReply\x12\n\n\x02id\x18\x01 \x01(\x04\x12-\n\x04\x63ode\x18\x02 \x01(\x0e\x32\x1f.protocol.NotificationReplyCode\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\t*\xa5\x02\n\x06\x41\x63tion\x12\x08\n\x04NONE\x10\x00\x12\x17\n\x13\x45NABLE_INTERCEPTION\x10\x01\x12\x18\n\x14\x44ISABLE_INTERCEPTION\x10\x02\x12\x13\n\x0f\x45NABLE_FIREWALL\x10\x03\x12\x14\n\x10\x44ISABLE_FIREWALL\x10\x04\x12\x13\n\x0fRELOAD_FW_RULES\x10\x05\x12\x11\n\rCHANGE_CONFIG\x10\x06\x12\x0f\n\x0b\x45NABLE_RULE\x10\x07\x12\x10\n\x0c\x44ISABLE_RULE\x10\x08\x12\x0f\n\x0b\x44\x45LETE_RULE\x10\t\x12\x0f\n\x0b\x43HANGE_RULE\x10\n\x12\r\n\tLOG_LEVEL\x10\x0b\x12\x08\n\x04STOP\x10\x0c\x12\x13\n\x0fMONITOR_PROCESS\x10\r\x12\x18\n\x14STOP_MONITOR_PROCESS\x10\x0e**\n\x15NotificationReplyCode\x12\x06\n\x02OK\x10\x00\x12\t\n\x05\x45RROR\x10\x01\x32\xaf\x02\n\x02UI\x12\x34\n\x04Ping\x12\x15.protocol.PingRequest\x1a\x13.protocol.PingReply\"\x00\x12\x31\n\x07\x41skRule\x12\x14.protocol.Connection\x1a\x0e.protocol.Rule\"\x00\x12=\n\tSubscribe\x12\x16.protocol.ClientConfig\x1a\x16.protocol.ClientConfig\"\x00\x12J\n\rNotifications\x12\x1b.protocol.NotificationReply\x1a\x16.protocol.Notification\"\x00(\x01\x30\x01\x12\x35\n\tPostAlert\x12\x0f.protocol.Alert\x1a\x15.protocol.MsgResponse\"\x00\x42\x35Z3github.com/evilsocket/opensnitch/daemon/ui/protocolb\x06proto3') +) + +_ACTION = _descriptor.EnumDescriptor( + name='Action', + full_name='protocol.Action', + filename=None, + file=DESCRIPTOR, + values=[ + _descriptor.EnumValueDescriptor( + name='NONE', index=0, number=0, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='ENABLE_INTERCEPTION', index=1, number=1, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='DISABLE_INTERCEPTION', index=2, number=2, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='ENABLE_FIREWALL', index=3, number=3, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='DISABLE_FIREWALL', index=4, number=4, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='RELOAD_FW_RULES', index=5, number=5, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='CHANGE_CONFIG', index=6, number=6, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='ENABLE_RULE', index=7, number=7, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='DISABLE_RULE', index=8, number=8, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='DELETE_RULE', index=9, number=9, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='CHANGE_RULE', index=10, number=10, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='LOG_LEVEL', index=11, number=11, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='STOP', index=12, number=12, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='MONITOR_PROCESS', index=13, number=13, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='STOP_MONITOR_PROCESS', index=14, number=14, + serialized_options=None, + type=None), + ], + containing_type=None, + serialized_options=None, + serialized_start=3795, + serialized_end=4088, +) +_sym_db.RegisterEnumDescriptor(_ACTION) + +Action = enum_type_wrapper.EnumTypeWrapper(_ACTION) +_NOTIFICATIONREPLYCODE = _descriptor.EnumDescriptor( + name='NotificationReplyCode', + full_name='protocol.NotificationReplyCode', + filename=None, + file=DESCRIPTOR, + values=[ + _descriptor.EnumValueDescriptor( + name='OK', index=0, number=0, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='ERROR', index=1, number=1, + serialized_options=None, + type=None), + ], + containing_type=None, + serialized_options=None, + serialized_start=4090, + serialized_end=4132, +) +_sym_db.RegisterEnumDescriptor(_NOTIFICATIONREPLYCODE) + +NotificationReplyCode = enum_type_wrapper.EnumTypeWrapper(_NOTIFICATIONREPLYCODE) +NONE = 0 +ENABLE_INTERCEPTION = 1 +DISABLE_INTERCEPTION = 2 +ENABLE_FIREWALL = 3 +DISABLE_FIREWALL = 4 +RELOAD_FW_RULES = 5 +CHANGE_CONFIG = 6 +ENABLE_RULE = 7 +DISABLE_RULE = 8 +DELETE_RULE = 9 +CHANGE_RULE = 10 +LOG_LEVEL = 11 +STOP = 12 +MONITOR_PROCESS = 13 +STOP_MONITOR_PROCESS = 14 +OK = 0 +ERROR = 1 + + +_ALERT_PRIORITY = _descriptor.EnumDescriptor( + name='Priority', + full_name='protocol.Alert.Priority', + filename=None, + file=DESCRIPTOR, + values=[ + _descriptor.EnumValueDescriptor( + name='LOW', index=0, number=0, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='MEDIUM', index=1, number=1, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='HIGH', index=2, number=2, + serialized_options=None, + type=None), + ], + containing_type=None, + serialized_options=None, + serialized_start=357, + serialized_end=398, +) +_sym_db.RegisterEnumDescriptor(_ALERT_PRIORITY) + +_ALERT_TYPE = _descriptor.EnumDescriptor( + name='Type', + full_name='protocol.Alert.Type', + filename=None, + file=DESCRIPTOR, + values=[ + _descriptor.EnumValueDescriptor( + name='ERROR', index=0, number=0, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='WARNING', index=1, number=1, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='INFO', index=2, number=2, + serialized_options=None, + type=None), + ], + containing_type=None, + serialized_options=None, + serialized_start=400, + serialized_end=440, +) +_sym_db.RegisterEnumDescriptor(_ALERT_TYPE) + +_ALERT_ACTION = _descriptor.EnumDescriptor( + name='Action', + full_name='protocol.Alert.Action', + filename=None, + file=DESCRIPTOR, + values=[ + _descriptor.EnumValueDescriptor( + name='NONE', index=0, number=0, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='SHOW_ALERT', index=1, number=1, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='SAVE_TO_DB', index=2, number=2, + serialized_options=None, + type=None), + ], + containing_type=None, + serialized_options=None, + serialized_start=442, + serialized_end=492, +) +_sym_db.RegisterEnumDescriptor(_ALERT_ACTION) + +_ALERT_WHAT = _descriptor.EnumDescriptor( + name='What', + full_name='protocol.Alert.What', + filename=None, + file=DESCRIPTOR, + values=[ + _descriptor.EnumValueDescriptor( + name='GENERIC', index=0, number=0, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='PROC_MONITOR', index=1, number=1, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='FIREWALL', index=2, number=2, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='CONNECTION', index=3, number=3, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='RULE', index=4, number=4, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='NETLINK', index=5, number=5, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='KERNEL_EVENT', index=6, number=6, + serialized_options=None, + type=None), + ], + containing_type=None, + serialized_options=None, + serialized_start=494, + serialized_end=602, +) +_sym_db.RegisterEnumDescriptor(_ALERT_WHAT) + + +_ALERT = _descriptor.Descriptor( + name='Alert', + full_name='protocol.Alert', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='id', full_name='protocol.Alert.id', index=0, + number=1, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='type', full_name='protocol.Alert.type', index=1, + number=2, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='action', full_name='protocol.Alert.action', index=2, + number=3, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='priority', full_name='protocol.Alert.priority', index=3, + number=4, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='what', full_name='protocol.Alert.what', index=4, + number=5, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='text', full_name='protocol.Alert.text', index=5, + number=6, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='proc', full_name='protocol.Alert.proc', index=6, + number=8, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='conn', full_name='protocol.Alert.conn', index=7, + number=9, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='rule', full_name='protocol.Alert.rule', index=8, + number=10, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='fwrule', full_name='protocol.Alert.fwrule', index=9, + number=11, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + _ALERT_PRIORITY, + _ALERT_TYPE, + _ALERT_ACTION, + _ALERT_WHAT, + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='data', full_name='protocol.Alert.data', + index=0, containing_type=None, fields=[]), + ], + serialized_start=23, + serialized_end=610, +) + + +_MSGRESPONSE = _descriptor.Descriptor( + name='MsgResponse', + full_name='protocol.MsgResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='id', full_name='protocol.MsgResponse.id', index=0, + number=1, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=612, + serialized_end=637, +) + + +_EVENT = _descriptor.Descriptor( + name='Event', + full_name='protocol.Event', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='time', full_name='protocol.Event.time', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='connection', full_name='protocol.Event.connection', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='rule', full_name='protocol.Event.rule', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='unixnano', full_name='protocol.Event.unixnano', index=3, + number=4, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=639, + serialized_end=750, +) + + +_STATISTICS_BYPROTOENTRY = _descriptor.Descriptor( + name='ByProtoEntry', + full_name='protocol.Statistics.ByProtoEntry', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='protocol.Statistics.ByProtoEntry.key', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='value', full_name='protocol.Statistics.ByProtoEntry.value', index=1, + number=2, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=_b('8\001'), + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1315, + serialized_end=1361, +) + +_STATISTICS_BYADDRESSENTRY = _descriptor.Descriptor( + name='ByAddressEntry', + full_name='protocol.Statistics.ByAddressEntry', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='protocol.Statistics.ByAddressEntry.key', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='value', full_name='protocol.Statistics.ByAddressEntry.value', index=1, + number=2, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=_b('8\001'), + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1363, + serialized_end=1411, +) + +_STATISTICS_BYHOSTENTRY = _descriptor.Descriptor( + name='ByHostEntry', + full_name='protocol.Statistics.ByHostEntry', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='protocol.Statistics.ByHostEntry.key', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='value', full_name='protocol.Statistics.ByHostEntry.value', index=1, + number=2, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=_b('8\001'), + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1413, + serialized_end=1458, +) + +_STATISTICS_BYPORTENTRY = _descriptor.Descriptor( + name='ByPortEntry', + full_name='protocol.Statistics.ByPortEntry', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='protocol.Statistics.ByPortEntry.key', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='value', full_name='protocol.Statistics.ByPortEntry.value', index=1, + number=2, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=_b('8\001'), + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1460, + serialized_end=1505, +) + +_STATISTICS_BYUIDENTRY = _descriptor.Descriptor( + name='ByUidEntry', + full_name='protocol.Statistics.ByUidEntry', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='protocol.Statistics.ByUidEntry.key', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='value', full_name='protocol.Statistics.ByUidEntry.value', index=1, + number=2, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=_b('8\001'), + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1507, + serialized_end=1551, +) + +_STATISTICS_BYEXECUTABLEENTRY = _descriptor.Descriptor( + name='ByExecutableEntry', + full_name='protocol.Statistics.ByExecutableEntry', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='protocol.Statistics.ByExecutableEntry.key', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='value', full_name='protocol.Statistics.ByExecutableEntry.value', index=1, + number=2, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=_b('8\001'), + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1553, + serialized_end=1604, +) + +_STATISTICS = _descriptor.Descriptor( + name='Statistics', + full_name='protocol.Statistics', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='daemon_version', full_name='protocol.Statistics.daemon_version', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='rules', full_name='protocol.Statistics.rules', index=1, + number=2, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='uptime', full_name='protocol.Statistics.uptime', index=2, + number=3, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='dns_responses', full_name='protocol.Statistics.dns_responses', index=3, + number=4, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='connections', full_name='protocol.Statistics.connections', index=4, + number=5, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='ignored', full_name='protocol.Statistics.ignored', index=5, + number=6, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='accepted', full_name='protocol.Statistics.accepted', index=6, + number=7, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='dropped', full_name='protocol.Statistics.dropped', index=7, + number=8, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='rule_hits', full_name='protocol.Statistics.rule_hits', index=8, + number=9, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='rule_misses', full_name='protocol.Statistics.rule_misses', index=9, + number=10, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='by_proto', full_name='protocol.Statistics.by_proto', index=10, + number=11, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='by_address', full_name='protocol.Statistics.by_address', index=11, + number=12, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='by_host', full_name='protocol.Statistics.by_host', index=12, + number=13, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='by_port', full_name='protocol.Statistics.by_port', index=13, + number=14, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='by_uid', full_name='protocol.Statistics.by_uid', index=14, + number=15, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='by_executable', full_name='protocol.Statistics.by_executable', index=15, + number=16, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='events', full_name='protocol.Statistics.events', index=16, + number=17, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[_STATISTICS_BYPROTOENTRY, _STATISTICS_BYADDRESSENTRY, _STATISTICS_BYHOSTENTRY, _STATISTICS_BYPORTENTRY, _STATISTICS_BYUIDENTRY, _STATISTICS_BYEXECUTABLEENTRY, ], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=753, + serialized_end=1604, +) + + +_PINGREQUEST = _descriptor.Descriptor( + name='PingRequest', + full_name='protocol.PingRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='id', full_name='protocol.PingRequest.id', index=0, + number=1, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='stats', full_name='protocol.PingRequest.stats', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1606, + serialized_end=1668, +) + + +_PINGREPLY = _descriptor.Descriptor( + name='PingReply', + full_name='protocol.PingReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='id', full_name='protocol.PingReply.id', index=0, + number=1, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1670, + serialized_end=1693, +) + + +_PROCESS_ENVENTRY = _descriptor.Descriptor( + name='EnvEntry', + full_name='protocol.Process.EnvEntry', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='protocol.Process.EnvEntry.key', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='value', full_name='protocol.Process.EnvEntry.value', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=_b('8\001'), + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1919, + serialized_end=1961, +) + +_PROCESS = _descriptor.Descriptor( + name='Process', + full_name='protocol.Process', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='pid', full_name='protocol.Process.pid', index=0, + number=1, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='ppid', full_name='protocol.Process.ppid', index=1, + number=2, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='uid', full_name='protocol.Process.uid', index=2, + number=3, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='comm', full_name='protocol.Process.comm', index=3, + number=4, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='path', full_name='protocol.Process.path', index=4, + number=5, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='args', full_name='protocol.Process.args', index=5, + number=6, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='env', full_name='protocol.Process.env', index=6, + number=7, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='cwd', full_name='protocol.Process.cwd', index=7, + number=8, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='io_reads', full_name='protocol.Process.io_reads', index=8, + number=9, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='io_writes', full_name='protocol.Process.io_writes', index=9, + number=10, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='net_reads', full_name='protocol.Process.net_reads', index=10, + number=11, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='net_writes', full_name='protocol.Process.net_writes', index=11, + number=12, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[_PROCESS_ENVENTRY, ], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1696, + serialized_end=1961, +) + + +_CONNECTION_PROCESSENVENTRY = _descriptor.Descriptor( + name='ProcessEnvEntry', + full_name='protocol.Connection.ProcessEnvEntry', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='protocol.Connection.ProcessEnvEntry.key', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='value', full_name='protocol.Connection.ProcessEnvEntry.value', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=_b('8\001'), + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2243, + serialized_end=2292, +) + +_CONNECTION = _descriptor.Descriptor( + name='Connection', + full_name='protocol.Connection', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='protocol', full_name='protocol.Connection.protocol', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='src_ip', full_name='protocol.Connection.src_ip', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='src_port', full_name='protocol.Connection.src_port', index=2, + number=3, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='dst_ip', full_name='protocol.Connection.dst_ip', index=3, + number=4, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='dst_host', full_name='protocol.Connection.dst_host', index=4, + number=5, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='dst_port', full_name='protocol.Connection.dst_port', index=5, + number=6, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='user_id', full_name='protocol.Connection.user_id', index=6, + number=7, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='process_id', full_name='protocol.Connection.process_id', index=7, + number=8, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='process_path', full_name='protocol.Connection.process_path', index=8, + number=9, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='process_cwd', full_name='protocol.Connection.process_cwd', index=9, + number=10, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='process_args', full_name='protocol.Connection.process_args', index=10, + number=11, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='process_env', full_name='protocol.Connection.process_env', index=11, + number=12, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[_CONNECTION_PROCESSENVENTRY, ], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1964, + serialized_end=2292, +) + + +_OPERATOR = _descriptor.Descriptor( + name='Operator', + full_name='protocol.Operator', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='type', full_name='protocol.Operator.type', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='operand', full_name='protocol.Operator.operand', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='data', full_name='protocol.Operator.data', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='sensitive', full_name='protocol.Operator.sensitive', index=3, + number=4, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='list', full_name='protocol.Operator.list', index=4, + number=5, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2294, + serialized_end=2402, +) + + +_RULE = _descriptor.Descriptor( + name='Rule', + full_name='protocol.Rule', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='created', full_name='protocol.Rule.created', index=0, + number=1, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='name', full_name='protocol.Rule.name', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='description', full_name='protocol.Rule.description', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='enabled', full_name='protocol.Rule.enabled', index=3, + number=4, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='precedence', full_name='protocol.Rule.precedence', index=4, + number=5, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='nolog', full_name='protocol.Rule.nolog', index=5, + number=6, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='action', full_name='protocol.Rule.action', index=6, + number=7, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='duration', full_name='protocol.Rule.duration', index=7, + number=8, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='operator', full_name='protocol.Rule.operator', index=8, + number=9, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2405, + serialized_end=2587, +) + + +_STATEMENTVALUES = _descriptor.Descriptor( + name='StatementValues', + full_name='protocol.StatementValues', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='Key', full_name='protocol.StatementValues.Key', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='Value', full_name='protocol.StatementValues.Value', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2589, + serialized_end=2634, +) + + +_STATEMENT = _descriptor.Descriptor( + name='Statement', + full_name='protocol.Statement', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='Op', full_name='protocol.Statement.Op', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='Name', full_name='protocol.Statement.Name', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='Values', full_name='protocol.Statement.Values', index=2, + number=3, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2636, + serialized_end=2716, +) + + +_EXPRESSIONS = _descriptor.Descriptor( + name='Expressions', + full_name='protocol.Expressions', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='Statement', full_name='protocol.Expressions.Statement', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2718, + serialized_end=2771, +) + + +_FWRULE = _descriptor.Descriptor( + name='FwRule', + full_name='protocol.FwRule', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='Table', full_name='protocol.FwRule.Table', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='Chain', full_name='protocol.FwRule.Chain', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='UUID', full_name='protocol.FwRule.UUID', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='Enabled', full_name='protocol.FwRule.Enabled', index=3, + number=4, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='Position', full_name='protocol.FwRule.Position', index=4, + number=5, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='Description', full_name='protocol.FwRule.Description', index=5, + number=6, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='Parameters', full_name='protocol.FwRule.Parameters', index=6, + number=7, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='Expressions', full_name='protocol.FwRule.Expressions', index=7, + number=8, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='Target', full_name='protocol.FwRule.Target', index=8, + number=9, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='TargetParameters', full_name='protocol.FwRule.TargetParameters', index=9, + number=10, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2774, + serialized_end=2988, +) + + +_FWCHAIN = _descriptor.Descriptor( + name='FwChain', + full_name='protocol.FwChain', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='Name', full_name='protocol.FwChain.Name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='Table', full_name='protocol.FwChain.Table', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='Family', full_name='protocol.FwChain.Family', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='Priority', full_name='protocol.FwChain.Priority', index=3, + number=4, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='Type', full_name='protocol.FwChain.Type', index=4, + number=5, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='Hook', full_name='protocol.FwChain.Hook', index=5, + number=6, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='Policy', full_name='protocol.FwChain.Policy', index=6, + number=7, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='Rules', full_name='protocol.FwChain.Rules', index=7, + number=8, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2991, + serialized_end=3140, +) + + +_FWCHAINS = _descriptor.Descriptor( + name='FwChains', + full_name='protocol.FwChains', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='Rule', full_name='protocol.FwChains.Rule', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='Chains', full_name='protocol.FwChains.Chains', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3142, + serialized_end=3219, +) + + +_SYSFIREWALL = _descriptor.Descriptor( + name='SysFirewall', + full_name='protocol.SysFirewall', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='Enabled', full_name='protocol.SysFirewall.Enabled', index=0, + number=1, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='Version', full_name='protocol.SysFirewall.Version', index=1, + number=2, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='SystemRules', full_name='protocol.SysFirewall.SystemRules', index=2, + number=3, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3221, + serialized_end=3309, +) + + +_CLIENTCONFIG = _descriptor.Descriptor( + name='ClientConfig', + full_name='protocol.ClientConfig', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='id', full_name='protocol.ClientConfig.id', index=0, + number=1, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='name', full_name='protocol.ClientConfig.name', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='version', full_name='protocol.ClientConfig.version', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='isFirewallRunning', full_name='protocol.ClientConfig.isFirewallRunning', index=3, + number=4, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='config', full_name='protocol.ClientConfig.config', index=4, + number=5, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='logLevel', full_name='protocol.ClientConfig.logLevel', index=5, + number=6, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='rules', full_name='protocol.ClientConfig.rules', index=6, + number=7, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='systemFirewall', full_name='protocol.ClientConfig.systemFirewall', index=7, + number=8, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3312, + serialized_end=3508, +) + + +_NOTIFICATION = _descriptor.Descriptor( + name='Notification', + full_name='protocol.Notification', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='id', full_name='protocol.Notification.id', index=0, + number=1, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='clientName', full_name='protocol.Notification.clientName', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='serverName', full_name='protocol.Notification.serverName', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='type', full_name='protocol.Notification.type', index=3, + number=4, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='data', full_name='protocol.Notification.data', index=4, + number=5, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='rules', full_name='protocol.Notification.rules', index=5, + number=6, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='sysFirewall', full_name='protocol.Notification.sysFirewall', index=6, + number=7, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3511, + serialized_end=3698, +) + + +_NOTIFICATIONREPLY = _descriptor.Descriptor( + name='NotificationReply', + full_name='protocol.NotificationReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='id', full_name='protocol.NotificationReply.id', index=0, + number=1, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='code', full_name='protocol.NotificationReply.code', index=1, + number=2, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='data', full_name='protocol.NotificationReply.data', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3700, + serialized_end=3792, +) + +_ALERT.fields_by_name['type'].enum_type = _ALERT_TYPE +_ALERT.fields_by_name['action'].enum_type = _ALERT_ACTION +_ALERT.fields_by_name['priority'].enum_type = _ALERT_PRIORITY +_ALERT.fields_by_name['what'].enum_type = _ALERT_WHAT +_ALERT.fields_by_name['proc'].message_type = _PROCESS +_ALERT.fields_by_name['conn'].message_type = _CONNECTION +_ALERT.fields_by_name['rule'].message_type = _RULE +_ALERT.fields_by_name['fwrule'].message_type = _FWRULE +_ALERT_PRIORITY.containing_type = _ALERT +_ALERT_TYPE.containing_type = _ALERT +_ALERT_ACTION.containing_type = _ALERT +_ALERT_WHAT.containing_type = _ALERT +_ALERT.oneofs_by_name['data'].fields.append( + _ALERT.fields_by_name['text']) +_ALERT.fields_by_name['text'].containing_oneof = _ALERT.oneofs_by_name['data'] +_ALERT.oneofs_by_name['data'].fields.append( + _ALERT.fields_by_name['proc']) +_ALERT.fields_by_name['proc'].containing_oneof = _ALERT.oneofs_by_name['data'] +_ALERT.oneofs_by_name['data'].fields.append( + _ALERT.fields_by_name['conn']) +_ALERT.fields_by_name['conn'].containing_oneof = _ALERT.oneofs_by_name['data'] +_ALERT.oneofs_by_name['data'].fields.append( + _ALERT.fields_by_name['rule']) +_ALERT.fields_by_name['rule'].containing_oneof = _ALERT.oneofs_by_name['data'] +_ALERT.oneofs_by_name['data'].fields.append( + _ALERT.fields_by_name['fwrule']) +_ALERT.fields_by_name['fwrule'].containing_oneof = _ALERT.oneofs_by_name['data'] +_EVENT.fields_by_name['connection'].message_type = _CONNECTION +_EVENT.fields_by_name['rule'].message_type = _RULE +_STATISTICS_BYPROTOENTRY.containing_type = _STATISTICS +_STATISTICS_BYADDRESSENTRY.containing_type = _STATISTICS +_STATISTICS_BYHOSTENTRY.containing_type = _STATISTICS +_STATISTICS_BYPORTENTRY.containing_type = _STATISTICS +_STATISTICS_BYUIDENTRY.containing_type = _STATISTICS +_STATISTICS_BYEXECUTABLEENTRY.containing_type = _STATISTICS +_STATISTICS.fields_by_name['by_proto'].message_type = _STATISTICS_BYPROTOENTRY +_STATISTICS.fields_by_name['by_address'].message_type = _STATISTICS_BYADDRESSENTRY +_STATISTICS.fields_by_name['by_host'].message_type = _STATISTICS_BYHOSTENTRY +_STATISTICS.fields_by_name['by_port'].message_type = _STATISTICS_BYPORTENTRY +_STATISTICS.fields_by_name['by_uid'].message_type = _STATISTICS_BYUIDENTRY +_STATISTICS.fields_by_name['by_executable'].message_type = _STATISTICS_BYEXECUTABLEENTRY +_STATISTICS.fields_by_name['events'].message_type = _EVENT +_PINGREQUEST.fields_by_name['stats'].message_type = _STATISTICS +_PROCESS_ENVENTRY.containing_type = _PROCESS +_PROCESS.fields_by_name['env'].message_type = _PROCESS_ENVENTRY +_CONNECTION_PROCESSENVENTRY.containing_type = _CONNECTION +_CONNECTION.fields_by_name['process_env'].message_type = _CONNECTION_PROCESSENVENTRY +_OPERATOR.fields_by_name['list'].message_type = _OPERATOR +_RULE.fields_by_name['operator'].message_type = _OPERATOR +_STATEMENT.fields_by_name['Values'].message_type = _STATEMENTVALUES +_EXPRESSIONS.fields_by_name['Statement'].message_type = _STATEMENT +_FWRULE.fields_by_name['Expressions'].message_type = _EXPRESSIONS +_FWCHAIN.fields_by_name['Rules'].message_type = _FWRULE +_FWCHAINS.fields_by_name['Rule'].message_type = _FWRULE +_FWCHAINS.fields_by_name['Chains'].message_type = _FWCHAIN +_SYSFIREWALL.fields_by_name['SystemRules'].message_type = _FWCHAINS +_CLIENTCONFIG.fields_by_name['rules'].message_type = _RULE +_CLIENTCONFIG.fields_by_name['systemFirewall'].message_type = _SYSFIREWALL +_NOTIFICATION.fields_by_name['type'].enum_type = _ACTION +_NOTIFICATION.fields_by_name['rules'].message_type = _RULE +_NOTIFICATION.fields_by_name['sysFirewall'].message_type = _SYSFIREWALL +_NOTIFICATIONREPLY.fields_by_name['code'].enum_type = _NOTIFICATIONREPLYCODE +DESCRIPTOR.message_types_by_name['Alert'] = _ALERT +DESCRIPTOR.message_types_by_name['MsgResponse'] = _MSGRESPONSE +DESCRIPTOR.message_types_by_name['Event'] = _EVENT +DESCRIPTOR.message_types_by_name['Statistics'] = _STATISTICS +DESCRIPTOR.message_types_by_name['PingRequest'] = _PINGREQUEST +DESCRIPTOR.message_types_by_name['PingReply'] = _PINGREPLY +DESCRIPTOR.message_types_by_name['Process'] = _PROCESS +DESCRIPTOR.message_types_by_name['Connection'] = _CONNECTION +DESCRIPTOR.message_types_by_name['Operator'] = _OPERATOR +DESCRIPTOR.message_types_by_name['Rule'] = _RULE +DESCRIPTOR.message_types_by_name['StatementValues'] = _STATEMENTVALUES +DESCRIPTOR.message_types_by_name['Statement'] = _STATEMENT +DESCRIPTOR.message_types_by_name['Expressions'] = _EXPRESSIONS +DESCRIPTOR.message_types_by_name['FwRule'] = _FWRULE +DESCRIPTOR.message_types_by_name['FwChain'] = _FWCHAIN +DESCRIPTOR.message_types_by_name['FwChains'] = _FWCHAINS +DESCRIPTOR.message_types_by_name['SysFirewall'] = _SYSFIREWALL +DESCRIPTOR.message_types_by_name['ClientConfig'] = _CLIENTCONFIG +DESCRIPTOR.message_types_by_name['Notification'] = _NOTIFICATION +DESCRIPTOR.message_types_by_name['NotificationReply'] = _NOTIFICATIONREPLY +DESCRIPTOR.enum_types_by_name['Action'] = _ACTION +DESCRIPTOR.enum_types_by_name['NotificationReplyCode'] = _NOTIFICATIONREPLYCODE +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +Alert = _reflection.GeneratedProtocolMessageType('Alert', (_message.Message,), { + 'DESCRIPTOR' : _ALERT, + '__module__' : 'ui_pb2' + # @@protoc_insertion_point(class_scope:protocol.Alert) + }) +_sym_db.RegisterMessage(Alert) + +MsgResponse = _reflection.GeneratedProtocolMessageType('MsgResponse', (_message.Message,), { + 'DESCRIPTOR' : _MSGRESPONSE, + '__module__' : 'ui_pb2' + # @@protoc_insertion_point(class_scope:protocol.MsgResponse) + }) +_sym_db.RegisterMessage(MsgResponse) + +Event = _reflection.GeneratedProtocolMessageType('Event', (_message.Message,), { + 'DESCRIPTOR' : _EVENT, + '__module__' : 'ui_pb2' + # @@protoc_insertion_point(class_scope:protocol.Event) + }) +_sym_db.RegisterMessage(Event) + +Statistics = _reflection.GeneratedProtocolMessageType('Statistics', (_message.Message,), { + + 'ByProtoEntry' : _reflection.GeneratedProtocolMessageType('ByProtoEntry', (_message.Message,), { + 'DESCRIPTOR' : _STATISTICS_BYPROTOENTRY, + '__module__' : 'ui_pb2' + # @@protoc_insertion_point(class_scope:protocol.Statistics.ByProtoEntry) + }) + , + + 'ByAddressEntry' : _reflection.GeneratedProtocolMessageType('ByAddressEntry', (_message.Message,), { + 'DESCRIPTOR' : _STATISTICS_BYADDRESSENTRY, + '__module__' : 'ui_pb2' + # @@protoc_insertion_point(class_scope:protocol.Statistics.ByAddressEntry) + }) + , + + 'ByHostEntry' : _reflection.GeneratedProtocolMessageType('ByHostEntry', (_message.Message,), { + 'DESCRIPTOR' : _STATISTICS_BYHOSTENTRY, + '__module__' : 'ui_pb2' + # @@protoc_insertion_point(class_scope:protocol.Statistics.ByHostEntry) + }) + , + + 'ByPortEntry' : _reflection.GeneratedProtocolMessageType('ByPortEntry', (_message.Message,), { + 'DESCRIPTOR' : _STATISTICS_BYPORTENTRY, + '__module__' : 'ui_pb2' + # @@protoc_insertion_point(class_scope:protocol.Statistics.ByPortEntry) + }) + , + + 'ByUidEntry' : _reflection.GeneratedProtocolMessageType('ByUidEntry', (_message.Message,), { + 'DESCRIPTOR' : _STATISTICS_BYUIDENTRY, + '__module__' : 'ui_pb2' + # @@protoc_insertion_point(class_scope:protocol.Statistics.ByUidEntry) + }) + , + + 'ByExecutableEntry' : _reflection.GeneratedProtocolMessageType('ByExecutableEntry', (_message.Message,), { + 'DESCRIPTOR' : _STATISTICS_BYEXECUTABLEENTRY, + '__module__' : 'ui_pb2' + # @@protoc_insertion_point(class_scope:protocol.Statistics.ByExecutableEntry) + }) + , + 'DESCRIPTOR' : _STATISTICS, + '__module__' : 'ui_pb2' + # @@protoc_insertion_point(class_scope:protocol.Statistics) + }) +_sym_db.RegisterMessage(Statistics) +_sym_db.RegisterMessage(Statistics.ByProtoEntry) +_sym_db.RegisterMessage(Statistics.ByAddressEntry) +_sym_db.RegisterMessage(Statistics.ByHostEntry) +_sym_db.RegisterMessage(Statistics.ByPortEntry) +_sym_db.RegisterMessage(Statistics.ByUidEntry) +_sym_db.RegisterMessage(Statistics.ByExecutableEntry) + +PingRequest = _reflection.GeneratedProtocolMessageType('PingRequest', (_message.Message,), { + 'DESCRIPTOR' : _PINGREQUEST, + '__module__' : 'ui_pb2' + # @@protoc_insertion_point(class_scope:protocol.PingRequest) + }) +_sym_db.RegisterMessage(PingRequest) + +PingReply = _reflection.GeneratedProtocolMessageType('PingReply', (_message.Message,), { + 'DESCRIPTOR' : _PINGREPLY, + '__module__' : 'ui_pb2' + # @@protoc_insertion_point(class_scope:protocol.PingReply) + }) +_sym_db.RegisterMessage(PingReply) + +Process = _reflection.GeneratedProtocolMessageType('Process', (_message.Message,), { + + 'EnvEntry' : _reflection.GeneratedProtocolMessageType('EnvEntry', (_message.Message,), { + 'DESCRIPTOR' : _PROCESS_ENVENTRY, + '__module__' : 'ui_pb2' + # @@protoc_insertion_point(class_scope:protocol.Process.EnvEntry) + }) + , + 'DESCRIPTOR' : _PROCESS, + '__module__' : 'ui_pb2' + # @@protoc_insertion_point(class_scope:protocol.Process) + }) +_sym_db.RegisterMessage(Process) +_sym_db.RegisterMessage(Process.EnvEntry) + +Connection = _reflection.GeneratedProtocolMessageType('Connection', (_message.Message,), { + + 'ProcessEnvEntry' : _reflection.GeneratedProtocolMessageType('ProcessEnvEntry', (_message.Message,), { + 'DESCRIPTOR' : _CONNECTION_PROCESSENVENTRY, + '__module__' : 'ui_pb2' + # @@protoc_insertion_point(class_scope:protocol.Connection.ProcessEnvEntry) + }) + , + 'DESCRIPTOR' : _CONNECTION, + '__module__' : 'ui_pb2' + # @@protoc_insertion_point(class_scope:protocol.Connection) + }) +_sym_db.RegisterMessage(Connection) +_sym_db.RegisterMessage(Connection.ProcessEnvEntry) + +Operator = _reflection.GeneratedProtocolMessageType('Operator', (_message.Message,), { + 'DESCRIPTOR' : _OPERATOR, + '__module__' : 'ui_pb2' + # @@protoc_insertion_point(class_scope:protocol.Operator) + }) +_sym_db.RegisterMessage(Operator) + +Rule = _reflection.GeneratedProtocolMessageType('Rule', (_message.Message,), { + 'DESCRIPTOR' : _RULE, + '__module__' : 'ui_pb2' + # @@protoc_insertion_point(class_scope:protocol.Rule) + }) +_sym_db.RegisterMessage(Rule) + +StatementValues = _reflection.GeneratedProtocolMessageType('StatementValues', (_message.Message,), { + 'DESCRIPTOR' : _STATEMENTVALUES, + '__module__' : 'ui_pb2' + # @@protoc_insertion_point(class_scope:protocol.StatementValues) + }) +_sym_db.RegisterMessage(StatementValues) + +Statement = _reflection.GeneratedProtocolMessageType('Statement', (_message.Message,), { + 'DESCRIPTOR' : _STATEMENT, + '__module__' : 'ui_pb2' + # @@protoc_insertion_point(class_scope:protocol.Statement) + }) +_sym_db.RegisterMessage(Statement) + +Expressions = _reflection.GeneratedProtocolMessageType('Expressions', (_message.Message,), { + 'DESCRIPTOR' : _EXPRESSIONS, + '__module__' : 'ui_pb2' + # @@protoc_insertion_point(class_scope:protocol.Expressions) + }) +_sym_db.RegisterMessage(Expressions) + +FwRule = _reflection.GeneratedProtocolMessageType('FwRule', (_message.Message,), { + 'DESCRIPTOR' : _FWRULE, + '__module__' : 'ui_pb2' + # @@protoc_insertion_point(class_scope:protocol.FwRule) + }) +_sym_db.RegisterMessage(FwRule) + +FwChain = _reflection.GeneratedProtocolMessageType('FwChain', (_message.Message,), { + 'DESCRIPTOR' : _FWCHAIN, + '__module__' : 'ui_pb2' + # @@protoc_insertion_point(class_scope:protocol.FwChain) + }) +_sym_db.RegisterMessage(FwChain) + +FwChains = _reflection.GeneratedProtocolMessageType('FwChains', (_message.Message,), { + 'DESCRIPTOR' : _FWCHAINS, + '__module__' : 'ui_pb2' + # @@protoc_insertion_point(class_scope:protocol.FwChains) + }) +_sym_db.RegisterMessage(FwChains) + +SysFirewall = _reflection.GeneratedProtocolMessageType('SysFirewall', (_message.Message,), { + 'DESCRIPTOR' : _SYSFIREWALL, + '__module__' : 'ui_pb2' + # @@protoc_insertion_point(class_scope:protocol.SysFirewall) + }) +_sym_db.RegisterMessage(SysFirewall) + +ClientConfig = _reflection.GeneratedProtocolMessageType('ClientConfig', (_message.Message,), { + 'DESCRIPTOR' : _CLIENTCONFIG, + '__module__' : 'ui_pb2' + # @@protoc_insertion_point(class_scope:protocol.ClientConfig) + }) +_sym_db.RegisterMessage(ClientConfig) + +Notification = _reflection.GeneratedProtocolMessageType('Notification', (_message.Message,), { + 'DESCRIPTOR' : _NOTIFICATION, + '__module__' : 'ui_pb2' + # @@protoc_insertion_point(class_scope:protocol.Notification) + }) +_sym_db.RegisterMessage(Notification) + +NotificationReply = _reflection.GeneratedProtocolMessageType('NotificationReply', (_message.Message,), { + 'DESCRIPTOR' : _NOTIFICATIONREPLY, + '__module__' : 'ui_pb2' + # @@protoc_insertion_point(class_scope:protocol.NotificationReply) + }) +_sym_db.RegisterMessage(NotificationReply) + + +DESCRIPTOR._options = None +_STATISTICS_BYPROTOENTRY._options = None +_STATISTICS_BYADDRESSENTRY._options = None +_STATISTICS_BYHOSTENTRY._options = None +_STATISTICS_BYPORTENTRY._options = None +_STATISTICS_BYUIDENTRY._options = None +_STATISTICS_BYEXECUTABLEENTRY._options = None +_PROCESS_ENVENTRY._options = None +_CONNECTION_PROCESSENVENTRY._options = None + +_UI = _descriptor.ServiceDescriptor( + name='UI', + full_name='protocol.UI', + file=DESCRIPTOR, + index=0, + serialized_options=None, + serialized_start=4135, + serialized_end=4438, + methods=[ + _descriptor.MethodDescriptor( + name='Ping', + full_name='protocol.UI.Ping', + index=0, + containing_service=None, + input_type=_PINGREQUEST, + output_type=_PINGREPLY, + serialized_options=None, + ), + _descriptor.MethodDescriptor( + name='AskRule', + full_name='protocol.UI.AskRule', + index=1, + containing_service=None, + input_type=_CONNECTION, + output_type=_RULE, + serialized_options=None, + ), + _descriptor.MethodDescriptor( + name='Subscribe', + full_name='protocol.UI.Subscribe', + index=2, + containing_service=None, + input_type=_CLIENTCONFIG, + output_type=_CLIENTCONFIG, + serialized_options=None, + ), + _descriptor.MethodDescriptor( + name='Notifications', + full_name='protocol.UI.Notifications', + index=3, + containing_service=None, + input_type=_NOTIFICATIONREPLY, + output_type=_NOTIFICATION, + serialized_options=None, + ), + _descriptor.MethodDescriptor( + name='PostAlert', + full_name='protocol.UI.PostAlert', + index=4, + containing_service=None, + input_type=_ALERT, + output_type=_MSGRESPONSE, + serialized_options=None, + ), +]) +_sym_db.RegisterServiceDescriptor(_UI) + +DESCRIPTOR.services_by_name['UI'] = _UI + +# @@protoc_insertion_point(module_scope) diff --git a/ui/opensnitch/proto/pre3200/ui_pb2_grpc.py b/ui/opensnitch/proto/pre3200/ui_pb2_grpc.py new file mode 100644 index 0000000..8dd3491 --- /dev/null +++ b/ui/opensnitch/proto/pre3200/ui_pb2_grpc.py @@ -0,0 +1,114 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +import grpc + +from . import ui_pb2 as ui__pb2 + + +class UIStub(object): + # missing associated documentation comment in .proto file + pass + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.Ping = channel.unary_unary( + '/protocol.UI/Ping', + request_serializer=ui__pb2.PingRequest.SerializeToString, + response_deserializer=ui__pb2.PingReply.FromString, + ) + self.AskRule = channel.unary_unary( + '/protocol.UI/AskRule', + request_serializer=ui__pb2.Connection.SerializeToString, + response_deserializer=ui__pb2.Rule.FromString, + ) + self.Subscribe = channel.unary_unary( + '/protocol.UI/Subscribe', + request_serializer=ui__pb2.ClientConfig.SerializeToString, + response_deserializer=ui__pb2.ClientConfig.FromString, + ) + self.Notifications = channel.stream_stream( + '/protocol.UI/Notifications', + request_serializer=ui__pb2.NotificationReply.SerializeToString, + response_deserializer=ui__pb2.Notification.FromString, + ) + self.PostAlert = channel.unary_unary( + '/protocol.UI/PostAlert', + request_serializer=ui__pb2.Alert.SerializeToString, + response_deserializer=ui__pb2.MsgResponse.FromString, + ) + + +class UIServicer(object): + # missing associated documentation comment in .proto file + pass + + def Ping(self, request, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def AskRule(self, request, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Subscribe(self, request, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Notifications(self, request_iterator, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def PostAlert(self, request, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_UIServicer_to_server(servicer, server): + rpc_method_handlers = { + 'Ping': grpc.unary_unary_rpc_method_handler( + servicer.Ping, + request_deserializer=ui__pb2.PingRequest.FromString, + response_serializer=ui__pb2.PingReply.SerializeToString, + ), + 'AskRule': grpc.unary_unary_rpc_method_handler( + servicer.AskRule, + request_deserializer=ui__pb2.Connection.FromString, + response_serializer=ui__pb2.Rule.SerializeToString, + ), + 'Subscribe': grpc.unary_unary_rpc_method_handler( + servicer.Subscribe, + request_deserializer=ui__pb2.ClientConfig.FromString, + response_serializer=ui__pb2.ClientConfig.SerializeToString, + ), + 'Notifications': grpc.stream_stream_rpc_method_handler( + servicer.Notifications, + request_deserializer=ui__pb2.NotificationReply.FromString, + response_serializer=ui__pb2.Notification.SerializeToString, + ), + 'PostAlert': grpc.unary_unary_rpc_method_handler( + servicer.PostAlert, + request_deserializer=ui__pb2.Alert.FromString, + response_serializer=ui__pb2.MsgResponse.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'protocol.UI', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) diff --git a/ui/opensnitch/proto/ui_pb2.py b/ui/opensnitch/proto/ui_pb2.py new file mode 100644 index 0000000..560b7a9 --- /dev/null +++ b/ui/opensnitch/proto/ui_pb2.py @@ -0,0 +1,110 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: ui.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x08ui.proto\x12\x08protocol\"\xcb\x04\n\x05\x41lert\x12\n\n\x02id\x18\x01 \x01(\x04\x12\"\n\x04type\x18\x02 \x01(\x0e\x32\x14.protocol.Alert.Type\x12&\n\x06\x61\x63tion\x18\x03 \x01(\x0e\x32\x16.protocol.Alert.Action\x12*\n\x08priority\x18\x04 \x01(\x0e\x32\x18.protocol.Alert.Priority\x12\"\n\x04what\x18\x05 \x01(\x0e\x32\x14.protocol.Alert.What\x12\x0e\n\x04text\x18\x06 \x01(\tH\x00\x12!\n\x04proc\x18\x08 \x01(\x0b\x32\x11.protocol.ProcessH\x00\x12$\n\x04\x63onn\x18\t \x01(\x0b\x32\x14.protocol.ConnectionH\x00\x12\x1e\n\x04rule\x18\n \x01(\x0b\x32\x0e.protocol.RuleH\x00\x12\"\n\x06\x66wrule\x18\x0b \x01(\x0b\x32\x10.protocol.FwRuleH\x00\")\n\x08Priority\x12\x07\n\x03LOW\x10\x00\x12\n\n\x06MEDIUM\x10\x01\x12\x08\n\x04HIGH\x10\x02\"(\n\x04Type\x12\t\n\x05\x45RROR\x10\x00\x12\x0b\n\x07WARNING\x10\x01\x12\x08\n\x04INFO\x10\x02\"2\n\x06\x41\x63tion\x12\x08\n\x04NONE\x10\x00\x12\x0e\n\nSHOW_ALERT\x10\x01\x12\x0e\n\nSAVE_TO_DB\x10\x02\"l\n\x04What\x12\x0b\n\x07GENERIC\x10\x00\x12\x10\n\x0cPROC_MONITOR\x10\x01\x12\x0c\n\x08\x46IREWALL\x10\x02\x12\x0e\n\nCONNECTION\x10\x03\x12\x08\n\x04RULE\x10\x04\x12\x0b\n\x07NETLINK\x10\x05\x12\x10\n\x0cKERNEL_EVENT\x10\x06\x42\x06\n\x04\x64\x61ta\"\x19\n\x0bMsgResponse\x12\n\n\x02id\x18\x01 \x01(\x04\"o\n\x05\x45vent\x12\x0c\n\x04time\x18\x01 \x01(\t\x12(\n\nconnection\x18\x02 \x01(\x0b\x32\x14.protocol.Connection\x12\x1c\n\x04rule\x18\x03 \x01(\x0b\x32\x0e.protocol.Rule\x12\x10\n\x08unixnano\x18\x04 \x01(\x03\"\xd3\x06\n\nStatistics\x12\x16\n\x0e\x64\x61\x65mon_version\x18\x01 \x01(\t\x12\r\n\x05rules\x18\x02 \x01(\x04\x12\x0e\n\x06uptime\x18\x03 \x01(\x04\x12\x15\n\rdns_responses\x18\x04 \x01(\x04\x12\x13\n\x0b\x63onnections\x18\x05 \x01(\x04\x12\x0f\n\x07ignored\x18\x06 \x01(\x04\x12\x10\n\x08\x61\x63\x63\x65pted\x18\x07 \x01(\x04\x12\x0f\n\x07\x64ropped\x18\x08 \x01(\x04\x12\x11\n\trule_hits\x18\t \x01(\x04\x12\x13\n\x0brule_misses\x18\n \x01(\x04\x12\x33\n\x08\x62y_proto\x18\x0b \x03(\x0b\x32!.protocol.Statistics.ByProtoEntry\x12\x37\n\nby_address\x18\x0c \x03(\x0b\x32#.protocol.Statistics.ByAddressEntry\x12\x31\n\x07\x62y_host\x18\r \x03(\x0b\x32 .protocol.Statistics.ByHostEntry\x12\x31\n\x07\x62y_port\x18\x0e \x03(\x0b\x32 .protocol.Statistics.ByPortEntry\x12/\n\x06\x62y_uid\x18\x0f \x03(\x0b\x32\x1f.protocol.Statistics.ByUidEntry\x12=\n\rby_executable\x18\x10 \x03(\x0b\x32&.protocol.Statistics.ByExecutableEntry\x12\x1f\n\x06\x65vents\x18\x11 \x03(\x0b\x32\x0f.protocol.Event\x1a.\n\x0c\x42yProtoEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a\x30\n\x0e\x42yAddressEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a-\n\x0b\x42yHostEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a-\n\x0b\x42yPortEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a,\n\nByUidEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a\x33\n\x11\x42yExecutableEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\">\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x04\x12#\n\x05stats\x18\x02 \x01(\x0b\x32\x14.protocol.Statistics\"\x17\n\tPingReply\x12\n\n\x02id\x18\x01 \x01(\x04\"\x89\x02\n\x07Process\x12\x0b\n\x03pid\x18\x01 \x01(\x04\x12\x0c\n\x04ppid\x18\x02 \x01(\x04\x12\x0b\n\x03uid\x18\x03 \x01(\x04\x12\x0c\n\x04\x63omm\x18\x04 \x01(\t\x12\x0c\n\x04path\x18\x05 \x01(\t\x12\x0c\n\x04\x61rgs\x18\x06 \x03(\t\x12\'\n\x03\x65nv\x18\x07 \x03(\x0b\x32\x1a.protocol.Process.EnvEntry\x12\x0b\n\x03\x63wd\x18\x08 \x01(\t\x12\x10\n\x08io_reads\x18\t \x01(\x04\x12\x11\n\tio_writes\x18\n \x01(\x04\x12\x11\n\tnet_reads\x18\x0b \x01(\x04\x12\x12\n\nnet_writes\x18\x0c \x01(\x04\x1a*\n\x08\x45nvEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xc8\x02\n\nConnection\x12\x10\n\x08protocol\x18\x01 \x01(\t\x12\x0e\n\x06src_ip\x18\x02 \x01(\t\x12\x10\n\x08src_port\x18\x03 \x01(\r\x12\x0e\n\x06\x64st_ip\x18\x04 \x01(\t\x12\x10\n\x08\x64st_host\x18\x05 \x01(\t\x12\x10\n\x08\x64st_port\x18\x06 \x01(\r\x12\x0f\n\x07user_id\x18\x07 \x01(\r\x12\x12\n\nprocess_id\x18\x08 \x01(\r\x12\x14\n\x0cprocess_path\x18\t \x01(\t\x12\x13\n\x0bprocess_cwd\x18\n \x01(\t\x12\x14\n\x0cprocess_args\x18\x0b \x03(\t\x12\x39\n\x0bprocess_env\x18\x0c \x03(\x0b\x32$.protocol.Connection.ProcessEnvEntry\x1a\x31\n\x0fProcessEnvEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"l\n\x08Operator\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0f\n\x07operand\x18\x02 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\t\x12\x11\n\tsensitive\x18\x04 \x01(\x08\x12 \n\x04list\x18\x05 \x03(\x0b\x32\x12.protocol.Operator\"\xb6\x01\n\x04Rule\x12\x0f\n\x07\x63reated\x18\x01 \x01(\x03\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\x12\x0f\n\x07\x65nabled\x18\x04 \x01(\x08\x12\x12\n\nprecedence\x18\x05 \x01(\x08\x12\r\n\x05nolog\x18\x06 \x01(\x08\x12\x0e\n\x06\x61\x63tion\x18\x07 \x01(\t\x12\x10\n\x08\x64uration\x18\x08 \x01(\t\x12$\n\x08operator\x18\t \x01(\x0b\x32\x12.protocol.Operator\"-\n\x0fStatementValues\x12\x0b\n\x03Key\x18\x01 \x01(\t\x12\r\n\x05Value\x18\x02 \x01(\t\"P\n\tStatement\x12\n\n\x02Op\x18\x01 \x01(\t\x12\x0c\n\x04Name\x18\x02 \x01(\t\x12)\n\x06Values\x18\x03 \x03(\x0b\x32\x19.protocol.StatementValues\"5\n\x0b\x45xpressions\x12&\n\tStatement\x18\x01 \x01(\x0b\x32\x13.protocol.Statement\"\xd6\x01\n\x06\x46wRule\x12\r\n\x05Table\x18\x01 \x01(\t\x12\r\n\x05\x43hain\x18\x02 \x01(\t\x12\x0c\n\x04UUID\x18\x03 \x01(\t\x12\x0f\n\x07\x45nabled\x18\x04 \x01(\x08\x12\x10\n\x08Position\x18\x05 \x01(\x04\x12\x13\n\x0b\x44\x65scription\x18\x06 \x01(\t\x12\x12\n\nParameters\x18\x07 \x01(\t\x12*\n\x0b\x45xpressions\x18\x08 \x03(\x0b\x32\x15.protocol.Expressions\x12\x0e\n\x06Target\x18\t \x01(\t\x12\x18\n\x10TargetParameters\x18\n \x01(\t\"\x95\x01\n\x07\x46wChain\x12\x0c\n\x04Name\x18\x01 \x01(\t\x12\r\n\x05Table\x18\x02 \x01(\t\x12\x0e\n\x06\x46\x61mily\x18\x03 \x01(\t\x12\x10\n\x08Priority\x18\x04 \x01(\t\x12\x0c\n\x04Type\x18\x05 \x01(\t\x12\x0c\n\x04Hook\x18\x06 \x01(\t\x12\x0e\n\x06Policy\x18\x07 \x01(\t\x12\x1f\n\x05Rules\x18\x08 \x03(\x0b\x32\x10.protocol.FwRule\"M\n\x08\x46wChains\x12\x1e\n\x04Rule\x18\x01 \x01(\x0b\x32\x10.protocol.FwRule\x12!\n\x06\x43hains\x18\x02 \x03(\x0b\x32\x11.protocol.FwChain\"X\n\x0bSysFirewall\x12\x0f\n\x07\x45nabled\x18\x01 \x01(\x08\x12\x0f\n\x07Version\x18\x02 \x01(\r\x12\'\n\x0bSystemRules\x18\x03 \x03(\x0b\x32\x12.protocol.FwChains\"\xc4\x01\n\x0c\x43lientConfig\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x19\n\x11isFirewallRunning\x18\x04 \x01(\x08\x12\x0e\n\x06\x63onfig\x18\x05 \x01(\t\x12\x10\n\x08logLevel\x18\x06 \x01(\r\x12\x1d\n\x05rules\x18\x07 \x03(\x0b\x32\x0e.protocol.Rule\x12-\n\x0esystemFirewall\x18\x08 \x01(\x0b\x32\x15.protocol.SysFirewall\"\xbb\x01\n\x0cNotification\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x12\n\nclientName\x18\x02 \x01(\t\x12\x12\n\nserverName\x18\x03 \x01(\t\x12\x1e\n\x04type\x18\x04 \x01(\x0e\x32\x10.protocol.Action\x12\x0c\n\x04\x64\x61ta\x18\x05 \x01(\t\x12\x1d\n\x05rules\x18\x06 \x03(\x0b\x32\x0e.protocol.Rule\x12*\n\x0bsysFirewall\x18\x07 \x01(\x0b\x32\x15.protocol.SysFirewall\"\\\n\x11NotificationReply\x12\n\n\x02id\x18\x01 \x01(\x04\x12-\n\x04\x63ode\x18\x02 \x01(\x0e\x32\x1f.protocol.NotificationReplyCode\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\t*\xa5\x02\n\x06\x41\x63tion\x12\x08\n\x04NONE\x10\x00\x12\x17\n\x13\x45NABLE_INTERCEPTION\x10\x01\x12\x18\n\x14\x44ISABLE_INTERCEPTION\x10\x02\x12\x13\n\x0f\x45NABLE_FIREWALL\x10\x03\x12\x14\n\x10\x44ISABLE_FIREWALL\x10\x04\x12\x13\n\x0fRELOAD_FW_RULES\x10\x05\x12\x11\n\rCHANGE_CONFIG\x10\x06\x12\x0f\n\x0b\x45NABLE_RULE\x10\x07\x12\x10\n\x0c\x44ISABLE_RULE\x10\x08\x12\x0f\n\x0b\x44\x45LETE_RULE\x10\t\x12\x0f\n\x0b\x43HANGE_RULE\x10\n\x12\r\n\tLOG_LEVEL\x10\x0b\x12\x08\n\x04STOP\x10\x0c\x12\x13\n\x0fMONITOR_PROCESS\x10\r\x12\x18\n\x14STOP_MONITOR_PROCESS\x10\x0e**\n\x15NotificationReplyCode\x12\x06\n\x02OK\x10\x00\x12\t\n\x05\x45RROR\x10\x01\x32\xaf\x02\n\x02UI\x12\x34\n\x04Ping\x12\x15.protocol.PingRequest\x1a\x13.protocol.PingReply\"\x00\x12\x31\n\x07\x41skRule\x12\x14.protocol.Connection\x1a\x0e.protocol.Rule\"\x00\x12=\n\tSubscribe\x12\x16.protocol.ClientConfig\x1a\x16.protocol.ClientConfig\"\x00\x12J\n\rNotifications\x12\x1b.protocol.NotificationReply\x1a\x16.protocol.Notification\"\x00(\x01\x30\x01\x12\x35\n\tPostAlert\x12\x0f.protocol.Alert\x1a\x15.protocol.MsgResponse\"\x00\x42\x35Z3github.com/evilsocket/opensnitch/daemon/ui/protocolb\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ui_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'Z3github.com/evilsocket/opensnitch/daemon/ui/protocol' + _STATISTICS_BYPROTOENTRY._options = None + _STATISTICS_BYPROTOENTRY._serialized_options = b'8\001' + _STATISTICS_BYADDRESSENTRY._options = None + _STATISTICS_BYADDRESSENTRY._serialized_options = b'8\001' + _STATISTICS_BYHOSTENTRY._options = None + _STATISTICS_BYHOSTENTRY._serialized_options = b'8\001' + _STATISTICS_BYPORTENTRY._options = None + _STATISTICS_BYPORTENTRY._serialized_options = b'8\001' + _STATISTICS_BYUIDENTRY._options = None + _STATISTICS_BYUIDENTRY._serialized_options = b'8\001' + _STATISTICS_BYEXECUTABLEENTRY._options = None + _STATISTICS_BYEXECUTABLEENTRY._serialized_options = b'8\001' + _PROCESS_ENVENTRY._options = None + _PROCESS_ENVENTRY._serialized_options = b'8\001' + _CONNECTION_PROCESSENVENTRY._options = None + _CONNECTION_PROCESSENVENTRY._serialized_options = b'8\001' + _ACTION._serialized_start=3795 + _ACTION._serialized_end=4088 + _NOTIFICATIONREPLYCODE._serialized_start=4090 + _NOTIFICATIONREPLYCODE._serialized_end=4132 + _ALERT._serialized_start=23 + _ALERT._serialized_end=610 + _ALERT_PRIORITY._serialized_start=357 + _ALERT_PRIORITY._serialized_end=398 + _ALERT_TYPE._serialized_start=400 + _ALERT_TYPE._serialized_end=440 + _ALERT_ACTION._serialized_start=442 + _ALERT_ACTION._serialized_end=492 + _ALERT_WHAT._serialized_start=494 + _ALERT_WHAT._serialized_end=602 + _MSGRESPONSE._serialized_start=612 + _MSGRESPONSE._serialized_end=637 + _EVENT._serialized_start=639 + _EVENT._serialized_end=750 + _STATISTICS._serialized_start=753 + _STATISTICS._serialized_end=1604 + _STATISTICS_BYPROTOENTRY._serialized_start=1315 + _STATISTICS_BYPROTOENTRY._serialized_end=1361 + _STATISTICS_BYADDRESSENTRY._serialized_start=1363 + _STATISTICS_BYADDRESSENTRY._serialized_end=1411 + _STATISTICS_BYHOSTENTRY._serialized_start=1413 + _STATISTICS_BYHOSTENTRY._serialized_end=1458 + _STATISTICS_BYPORTENTRY._serialized_start=1460 + _STATISTICS_BYPORTENTRY._serialized_end=1505 + _STATISTICS_BYUIDENTRY._serialized_start=1507 + _STATISTICS_BYUIDENTRY._serialized_end=1551 + _STATISTICS_BYEXECUTABLEENTRY._serialized_start=1553 + _STATISTICS_BYEXECUTABLEENTRY._serialized_end=1604 + _PINGREQUEST._serialized_start=1606 + _PINGREQUEST._serialized_end=1668 + _PINGREPLY._serialized_start=1670 + _PINGREPLY._serialized_end=1693 + _PROCESS._serialized_start=1696 + _PROCESS._serialized_end=1961 + _PROCESS_ENVENTRY._serialized_start=1919 + _PROCESS_ENVENTRY._serialized_end=1961 + _CONNECTION._serialized_start=1964 + _CONNECTION._serialized_end=2292 + _CONNECTION_PROCESSENVENTRY._serialized_start=2243 + _CONNECTION_PROCESSENVENTRY._serialized_end=2292 + _OPERATOR._serialized_start=2294 + _OPERATOR._serialized_end=2402 + _RULE._serialized_start=2405 + _RULE._serialized_end=2587 + _STATEMENTVALUES._serialized_start=2589 + _STATEMENTVALUES._serialized_end=2634 + _STATEMENT._serialized_start=2636 + _STATEMENT._serialized_end=2716 + _EXPRESSIONS._serialized_start=2718 + _EXPRESSIONS._serialized_end=2771 + _FWRULE._serialized_start=2774 + _FWRULE._serialized_end=2988 + _FWCHAIN._serialized_start=2991 + _FWCHAIN._serialized_end=3140 + _FWCHAINS._serialized_start=3142 + _FWCHAINS._serialized_end=3219 + _SYSFIREWALL._serialized_start=3221 + _SYSFIREWALL._serialized_end=3309 + _CLIENTCONFIG._serialized_start=3312 + _CLIENTCONFIG._serialized_end=3508 + _NOTIFICATION._serialized_start=3511 + _NOTIFICATION._serialized_end=3698 + _NOTIFICATIONREPLY._serialized_start=3700 + _NOTIFICATIONREPLY._serialized_end=3792 + _UI._serialized_start=4135 + _UI._serialized_end=4438 +# @@protoc_insertion_point(module_scope) diff --git a/ui/opensnitch/proto/ui_pb2_grpc.py b/ui/opensnitch/proto/ui_pb2_grpc.py new file mode 100644 index 0000000..4e3a786 --- /dev/null +++ b/ui/opensnitch/proto/ui_pb2_grpc.py @@ -0,0 +1,198 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc + +from . import ui_pb2 as ui__pb2 + + +class UIStub(object): + """Missing associated documentation comment in .proto file.""" + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.Ping = channel.unary_unary( + '/protocol.UI/Ping', + request_serializer=ui__pb2.PingRequest.SerializeToString, + response_deserializer=ui__pb2.PingReply.FromString, + ) + self.AskRule = channel.unary_unary( + '/protocol.UI/AskRule', + request_serializer=ui__pb2.Connection.SerializeToString, + response_deserializer=ui__pb2.Rule.FromString, + ) + self.Subscribe = channel.unary_unary( + '/protocol.UI/Subscribe', + request_serializer=ui__pb2.ClientConfig.SerializeToString, + response_deserializer=ui__pb2.ClientConfig.FromString, + ) + self.Notifications = channel.stream_stream( + '/protocol.UI/Notifications', + request_serializer=ui__pb2.NotificationReply.SerializeToString, + response_deserializer=ui__pb2.Notification.FromString, + ) + self.PostAlert = channel.unary_unary( + '/protocol.UI/PostAlert', + request_serializer=ui__pb2.Alert.SerializeToString, + response_deserializer=ui__pb2.MsgResponse.FromString, + ) + + +class UIServicer(object): + """Missing associated documentation comment in .proto file.""" + + def Ping(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def AskRule(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Subscribe(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Notifications(self, request_iterator, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def PostAlert(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_UIServicer_to_server(servicer, server): + rpc_method_handlers = { + 'Ping': grpc.unary_unary_rpc_method_handler( + servicer.Ping, + request_deserializer=ui__pb2.PingRequest.FromString, + response_serializer=ui__pb2.PingReply.SerializeToString, + ), + 'AskRule': grpc.unary_unary_rpc_method_handler( + servicer.AskRule, + request_deserializer=ui__pb2.Connection.FromString, + response_serializer=ui__pb2.Rule.SerializeToString, + ), + 'Subscribe': grpc.unary_unary_rpc_method_handler( + servicer.Subscribe, + request_deserializer=ui__pb2.ClientConfig.FromString, + response_serializer=ui__pb2.ClientConfig.SerializeToString, + ), + 'Notifications': grpc.stream_stream_rpc_method_handler( + servicer.Notifications, + request_deserializer=ui__pb2.NotificationReply.FromString, + response_serializer=ui__pb2.Notification.SerializeToString, + ), + 'PostAlert': grpc.unary_unary_rpc_method_handler( + servicer.PostAlert, + request_deserializer=ui__pb2.Alert.FromString, + response_serializer=ui__pb2.MsgResponse.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'protocol.UI', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + + + # This class is part of an EXPERIMENTAL API. +class UI(object): + """Missing associated documentation comment in .proto file.""" + + @staticmethod + def Ping(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/protocol.UI/Ping', + ui__pb2.PingRequest.SerializeToString, + ui__pb2.PingReply.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def AskRule(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/protocol.UI/AskRule', + ui__pb2.Connection.SerializeToString, + ui__pb2.Rule.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def Subscribe(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/protocol.UI/Subscribe', + ui__pb2.ClientConfig.SerializeToString, + ui__pb2.ClientConfig.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def Notifications(request_iterator, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.stream_stream(request_iterator, target, '/protocol.UI/Notifications', + ui__pb2.NotificationReply.SerializeToString, + ui__pb2.Notification.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def PostAlert(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/protocol.UI/PostAlert', + ui__pb2.Alert.SerializeToString, + ui__pb2.MsgResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/ui/opensnitch/res/__init__.py b/ui/opensnitch/res/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ui/opensnitch/res/firewall.ui b/ui/opensnitch/res/firewall.ui new file mode 100644 index 0000000..21a7dec --- /dev/null +++ b/ui/opensnitch/res/firewall.ui @@ -0,0 +1,520 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Dialog</class> + <widget class="QDialog" name="Dialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>440</width> + <height>463</height> + </rect> + </property> + <property name="windowTitle"> + <string>Firewall</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QTabWidget" name="tabWidget"> + <property name="styleSheet"> + <string notr="true">QTabBar { + alignment: center; +}</string> + </property> + <property name="tabPosition"> + <enum>QTabWidget::South</enum> + </property> + <property name="currentIndex"> + <number>0</number> + </property> + <property name="documentMode"> + <bool>true</bool> + </property> + <widget class="QWidget" name="tabMain"> + <attribute name="title"> + <string/> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <property name="leftMargin"> + <number>10</number> + </property> + <property name="topMargin"> + <number>5</number> + </property> + <property name="rightMargin"> + <number>10</number> + </property> + <property name="bottomMargin"> + <number>10</number> + </property> + <item> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Firewall</span></p></body></html></string> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_3"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QSlider" name="sliderFwEnable"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>48</width> + <height>16777215</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">QSlider::groove:horizontal { + background-color: transparent; + border: 0px solid #424242; + height: 20px; + border-radius: 4px; +} + +QSlider::handle:horizontal { + background-color: rgb(154, 153, 150); + border: 1px solid rgb(119, 118, 123); + width: 20px; + height: 20px; + line-height: 20px; /* do not delete this */ + /*margin-left: -5px;*/ + margin-top: -2px; + margin-bottom: -2px; + border-radius: 5px; +} + +QSlider::handle:horizontal:hover { + background-color: rgb(79, 91, 98); + border-radius: 5px; +} + +QSlider::add-page:horizontal { + border-radius: 4px; + background: rgb(237, 51, 59); + border: 1px solid rgb(192, 28, 40); +} + +QSlider::sub-page:horizontal { + border-radius: 4px; + background: rgb(139, 195, 74); + border: 1px solid rgb(67, 190, 24); +}</string> + </property> + <property name="maximum"> + <number>1</number> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="invertedAppearance"> + <bool>false</bool> + </property> + <property name="tickPosition"> + <enum>QSlider::TicksBothSides</enum> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QLabel" name="lblStatusIcon"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="textFormat"> + <enum>Qt::AutoText</enum> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + <property name="openExternalLinks"> + <bool>true</bool> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse</set> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_4"> + <item> + <spacer name="horizontalSpacer_4"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="Line" name="line"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>96</width> + <height>0</height> + </size> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_5"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item> + <widget class="QLabel" name="lblFwStatus"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="styleSheet"> + <string notr="true">color: rgb(237, 51, 59);</string> + </property> + <property name="text"> + <string/> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QGroupBox" name="policiesBox"> + <property name="title"> + <string/> + </property> + <property name="flat"> + <bool>true</bool> + </property> + <layout class="QGridLayout" name="gridLayout"> + <property name="leftMargin"> + <number>15</number> + </property> + <property name="topMargin"> + <number>5</number> + </property> + <property name="rightMargin"> + <number>15</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item row="4" column="0" colspan="2"> + <widget class="Line" name="line_2"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="lblProfile"> + <property name="text"> + <string>Profile</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QComboBox" name="comboInput"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <item> + <property name="text"> + <string>Allow</string> + </property> + <property name="icon"> + <iconset theme="emblem-default"> + <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset> + </property> + </item> + <item> + <property name="text"> + <string>Deny</string> + </property> + <property name="icon"> + <iconset theme="process-stop"> + <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset> + </property> + </item> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_4"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Outbound</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Inbound</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QComboBox" name="comboOutput"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <item> + <property name="text"> + <string>Allow</string> + </property> + <property name="icon"> + <iconset theme="emblem-default"> + <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset> + </property> + </item> + <item> + <property name="text"> + <string>Deny</string> + </property> + <property name="icon"> + <iconset theme="process-stop"> + <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset> + </property> + </item> + </widget> + </item> + <item row="0" column="1"> + <widget class="QComboBox" name="comboProfile"/> + </item> + <item row="5" column="0" colspan="2"> + <layout class="QGridLayout" name="gridLayout_2"> + <property name="topMargin"> + <number>5</number> + </property> + <property name="bottomMargin"> + <number>5</number> + </property> + <item row="0" column="0"> + <widget class="QPushButton" name="cmdAllowINService"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Allow inbound connections to a port</string> + </property> + <property name="text"> + <string>Allow service (IN)</string> + </property> + <property name="icon"> + <iconset theme="go-down"> + <normaloff>.</normaloff>.</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QPushButton" name="cmdAllowOUTService"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Exclude outbound connections to a port from being intercepted</string> + </property> + <property name="layoutDirection"> + <enum>Qt::RightToLeft</enum> + </property> + <property name="styleSheet"> + <string notr="true">QPushButton { text-align: right; }</string> + </property> + <property name="text"> + <string>Allow service (OUT)</string> + </property> + <property name="icon"> + <iconset theme="go-up"> + <normaloff>.</normaloff>.</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="1" column="0" colspan="2"> + <widget class="QPushButton" name="cmdNewRule"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>New rule</string> + </property> + <property name="icon"> + <iconset theme="document-new"> + <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_5"> + <item> + <widget class="QLabel" name="statusLabel"> + <property name="text"> + <string/> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + <property name="textInteractionFlags"> + <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QPushButton" name="cmdHelp"> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="help-browser"> + <normaloff>.</normaloff>.</iconset> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="cmdClose"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Close</string> + </property> + <property name="icon"> + <iconset theme="window-close"> + <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <tabstops> + <tabstop>sliderFwEnable</tabstop> + <tabstop>comboInput</tabstop> + <tabstop>comboOutput</tabstop> + <tabstop>cmdClose</tabstop> + <tabstop>tabWidget</tabstop> + </tabstops> + <resources/> + <connections/> +</ui> diff --git a/ui/opensnitch/res/firewall_rule.ui b/ui/opensnitch/res/firewall_rule.ui new file mode 100644 index 0000000..728d467 --- /dev/null +++ b/ui/opensnitch/res/firewall_rule.ui @@ -0,0 +1,479 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Dialog</class> + <widget class="QDialog" name="Dialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>484</width> + <height>600</height> + </rect> + </property> + <property name="windowTitle"> + <string>Firewall rule</string> + </property> + <property name="windowIcon"> + <iconset> + <normaloff>../../../../../../.designer/backup/icon-white.svg</normaloff>../../../../../../.designer/backup/icon-white.svg</iconset> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QLabel" name="label_7"> + <property name="text"> + <string>Node</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="comboNodes"/> + </item> + </layout> + </item> + <item> + <widget class="QCheckBox" name="checkEnable"> + <property name="text"> + <string>Enable</string> + </property> + </widget> + </item> + <item> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <property name="sizeConstraint"> + <enum>QLayout::SetMaximumSize</enum> + </property> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Description</string> + </property> + <property name="alignment"> + <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="lineDescription"> + <property name="clearButtonEnabled"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QTabWidget" name="tabWidget"> + <property name="styleSheet"> + <string notr="true">QTabBar { + alignment: center; +}</string> + </property> + <property name="tabPosition"> + <enum>QTabWidget::South</enum> + </property> + <property name="currentIndex"> + <number>0</number> + </property> + <property name="elideMode"> + <enum>Qt::ElideRight</enum> + </property> + <property name="documentMode"> + <bool>true</bool> + </property> + <property name="tabBarAutoHide"> + <bool>true</bool> + </property> + <widget class="QWidget" name="tabSimple"> + <attribute name="title"> + <string>Simple</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QToolBox" name="toolBoxSimple"> + <property name="currentIndex"> + <number>0</number> + </property> + <property name="tabSpacing"> + <number>0</number> + </property> + <widget class="QWidget" name="page1"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>448</width> + <height>200</height> + </rect> + </property> + <attribute name="label"> + <string/> + </attribute> + </widget> + </widget> + </item> + <item> + <widget class="QLabel" name="lblExcludeTip"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + </layout> + </widget> + </widget> + </item> + <item> + <widget class="QWidget" name="hboxAdvanced" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout_5"> + <property name="topMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QPushButton" name="cmdAddStatement"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Add new condition</string> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="list-add"> + <normaloff>.</normaloff>.</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="cmdDelStatement"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Remove selected condition</string> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="list-remove"> + <normaloff>.</normaloff>.</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QFrame" name="frameDirection"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Plain</enum> + </property> + <property name="lineWidth"> + <number>0</number> + </property> + <layout class="QVBoxLayout" name="verticalLayout_6"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Direction</string> + </property> + <property name="alignment"> + <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="comboDirection"> + <item> + <property name="text"> + <string>IN</string> + </property> + <property name="icon"> + <iconset theme="go-down"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + </item> + <item> + <property name="text"> + <string>OUT</string> + </property> + <property name="icon"> + <iconset theme="go-up"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + </item> + <item> + <property name="text"> + <string>FORWARD</string> + </property> + </item> + <item> + <property name="text"> + <string>PREROUTING</string> + </property> + </item> + <item> + <property name="text"> + <string>POSTROUTING</string> + </property> + </item> + </widget> + </item> + <item> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>Action</string> + </property> + <property name="alignment"> + <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="comboVerdict"> + <item> + <property name="text"> + <string>ACCEPT</string> + </property> + <property name="icon"> + <iconset theme="emblem-default"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + </item> + <item> + <property name="text"> + <string>DROP</string> + </property> + <property name="icon"> + <iconset theme="window-close"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + </item> + <item> + <property name="text"> + <string>REJECT</string> + </property> + <property name="icon"> + <iconset theme="edit-delete"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + </item> + <item> + <property name="text"> + <string>RETURN</string> + </property> + <property name="icon"> + <iconset theme="edit-undo"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + </item> + <item> + <property name="text"> + <string>QUEUE</string> + </property> + <property name="icon"> + <iconset theme="go-next"/> + </property> + </item> + <item> + <property name="text"> + <string>DNAT</string> + </property> + </item> + <item> + <property name="text"> + <string>SNAT</string> + </property> + </item> + <item> + <property name="text"> + <string>REDIRECT</string> + </property> + <property name="icon"> + <iconset theme="edit-redo"/> + </property> + </item> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QComboBox" name="comboVerdictParms"/> + </item> + <item> + <widget class="QLineEdit" name="lineVerdictParms"> + <property name="toolTip"> + <string>depending on the Action (i.e.: target), the syntaxis of the parameters will vary. +Some examples: + +QUEUE -> num 0 (or 1, 2, ...) +REDIRECT, TPROXY, DNAT, SNAT, MASQUERADE: + to :22 + to 192.168.1.254:8080 + to 192.168.1.254 + to 1024-2048 (masquerade)</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="Line" name="line"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="statusLabel"> + <property name="text"> + <string/> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QPushButton" name="helpButton"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="help-browser"> + <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="cmdClose"> + <property name="text"> + <string>Close</string> + </property> + <property name="icon"> + <iconset theme="window-close"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="cmdReset"> + <property name="text"> + <string>Clear</string> + </property> + <property name="icon"> + <iconset theme="reload"> + <normaloff>.</normaloff>.</iconset> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="cmdDelete"> + <property name="text"> + <string>Delete</string> + </property> + <property name="icon"> + <iconset theme="edit-delete"> + <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="cmdSave"> + <property name="text"> + <string>Save</string> + </property> + <property name="icon"> + <iconset theme="document-save"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="cmdAdd"> + <property name="text"> + <string>Add</string> + </property> + <property name="icon"> + <iconset theme="list-add"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/ui/opensnitch/res/icon-alert.png b/ui/opensnitch/res/icon-alert.png new file mode 100644 index 0000000..77cb571 Binary files /dev/null and b/ui/opensnitch/res/icon-alert.png differ diff --git a/ui/opensnitch/res/icon-off.png b/ui/opensnitch/res/icon-off.png new file mode 100644 index 0000000..e33aced Binary files /dev/null and b/ui/opensnitch/res/icon-off.png differ diff --git a/ui/opensnitch/res/icon-pause.png b/ui/opensnitch/res/icon-pause.png new file mode 100644 index 0000000..436df98 Binary files /dev/null and b/ui/opensnitch/res/icon-pause.png differ diff --git a/ui/opensnitch/res/icon-pause.svg b/ui/opensnitch/res/icon-pause.svg new file mode 100644 index 0000000..9f61111 --- /dev/null +++ b/ui/opensnitch/res/icon-pause.svg @@ -0,0 +1,109 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + width="512" + height="512" + viewBox="0 0 135.46667 135.46667" + version="1.1" + id="svg8" + inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)" + sodipodi:docname="icon-pause.svg" + inkscape:export-filename="/home/ga/Proiektuak/opensnitch/gui/opensnitch/ui/opensnitch/res/icon-pause.png" + inkscape:export-xdpi="96" + inkscape:export-ydpi="96" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:dc="http://purl.org/dc/elements/1.1/"> + <defs + id="defs2" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="1.8520834" + inkscape:cx="92.868389" + inkscape:cy="235.9505" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="false" + inkscape:window-width="1600" + inkscape:window-height="847" + inkscape:window-x="0" + inkscape:window-y="204" + inkscape:window-maximized="1" + units="px" + inkscape:document-rotation="0" + showguides="true" + inkscape:guide-bbox="true" + inkscape:pagecheckerboard="0"> + <sodipodi:guide + position="45.643586,19.473337" + orientation="0,-1" + id="guide858" /> + <sodipodi:guide + position="48.574825,118.50736" + orientation="0,-1" + id="guide860" /> + </sodipodi:namedview> + <metadata + id="metadata5"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Capa 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(0,-284.30001)"> + <path + style="fill:#232629;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.88352px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 24.756487,346.52813 9.319538,-24.69115 25.892395,-5.7321 24.204196,-12.72968 25.173834,11.71117 4.05241,24.92736 16.3729,12.59899 3.85152,17.78296 -6.72818,16.22266 -13.39906,8.50947 -91.980588,0.9444 -15.4585065,-11.35245 -3.13783,-17.10855 7.8853195,-15.623 z" + id="path1481" + sodipodi:nodetypes="ccccccccccccccc" + inkscape:export-filename="/home/ga/Proiektuak/opensnitch/gui/opensnitch/ui/lib/python3.9/site-packages/opensnitch/res/icon-white.png" + inkscape:export-xdpi="96" + inkscape:export-ydpi="96" /> + <path + style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.260914;stroke-opacity:1" + d="M 25.617431,400.30128 C 13.956756,398.62993 4.455433,390.16178 1.3179549,378.64484 c -0.61010168,-2.23995 -0.68141103,-2.97449 -0.68141103,-7.02809 0,-4.05349 0.0709898,-4.78804 0.68141103,-7.02767 1.4422687,-5.29433 4.0049287,-9.7113 7.7967237,-13.43793 3.1792184,-3.12477 7.6935334,-5.90721 11.3127154,-6.97315 l 1.011141,-0.29572 0.159592,-2.88031 c 0.699721,-12.63666 9.525535,-23.14365 22.243579,-26.48047 2.251529,-0.59144 3.06051,-0.67035 7.018915,-0.67878 2.525469,-0.006 4.987882,0.11997 5.653762,0.28414 1.179544,0.29572 1.179787,0.29572 1.94729,-0.66193 1.527585,-1.90257 4.987766,-4.99031 7.28526,-6.50045 5.401898,-3.5513 10.887485,-5.3302 17.382981,-5.63665 5.770238,-0.26941 10.984996,0.78506 16.16673,3.27379 7.128836,3.42523 12.181556,8.39142 15.666736,15.39805 2.50603,5.03807 3.59513,10.17644 3.34371,15.77554 l -0.13051,2.91272 2.13481,1.38154 c 1.17413,0.7598 3.3948,2.61049 4.93483,4.112 8.71561,8.49782 11.93955,20.33805 8.75495,32.15396 -1.29904,4.81951 -4.90315,10.89827 -8.63914,14.56995 -3.56956,3.50836 -9.80593,7.14911 -14.36425,8.38564 -4.82636,1.30967 -4.22578,1.29325 -45.398785,1.26083 -21.133453,-0.021 -39.125022,-0.13154 -39.981254,-0.25467 z m 81.947239,-8.71712 c 5.8504,-1.53751 11.05542,-5.03703 14.56544,-9.79276 1.44781,-1.96191 3.2449,-5.86701 3.9156,-8.50866 0.82755,-3.25884 0.82928,-8.34365 0.004,-11.5926 -1.99124,-7.83999 -8.14631,-14.63425 -15.64619,-17.27125 -1.49953,-0.52724 -2.28209,-0.9303 -2.20209,-1.1355 2.47536,-6.33996 2.20303,-13.3187 -0.75958,-19.46544 -2.59443,-5.38335 -6.55609,-9.27909 -12.007089,-11.8076 -3.90889,-1.81322 -6.232969,-2.31309 -10.75536,-2.31309 -3.127696,0 -4.219681,0.10629 -6.053166,0.58617 -7.241285,1.89288 -13.31075,6.68923 -16.465647,13.01161 l -0.732485,1.46794 -1.204478,-0.5988 c -2.240975,-1.11298 -5.542791,-1.97328 -8.178424,-2.13124 -9.130628,-0.54723 -18.012897,5.39587 -20.93206,14.00441 -1.653072,4.87486 -1.486231,9.52955 0.524816,14.64108 0.134869,0.34097 -0.08673,0.38411 -1.535235,0.29256 -2.040978,-0.12839 -5.359579,0.4904 -7.966056,1.4852 -5.358562,2.0439 -10.218999,7.06398 -12.038096,12.43418 -1.6865107,4.9784 -1.5773642,9.50883 0.346691,14.39252 2.080429,5.27991 7.182215,10.04153 12.692712,11.84623 1.10934,0.36201 2.648952,0.76296 3.421359,0.8924 0.794802,0.13259 18.196341,0.21573 40.092031,0.19047 l 38.687677,-0.0421 z" + id="path826" + inkscape:connector-curvature="0" + sodipodi:nodetypes="ccsccccccccccccsccccccccccccccscccsccccsscccccccccc" /> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:76.6842px;line-height:47.9278px;font-family:Korataki;-inkscape-font-specification:Korataki;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.91711px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="34.512127" + y="456.6312" + id="text841" + transform="scale(1.1975484,0.83503932)"><tspan + sodipodi:role="line" + id="tspan839" + x="34.512127" + y="456.6312" + style="fill:#ffffff;stroke-width:1.91711px">I</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:76.6842px;line-height:47.9278px;font-family:Korataki;-inkscape-font-specification:Korataki;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.91711px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="61.164921" + y="456.6312" + id="text841-7" + transform="scale(1.1975484,0.83503932)"><tspan + sodipodi:role="line" + id="tspan839-6" + x="61.164921" + y="456.6312" + style="fill:#ffffff;stroke-width:1.91711px">I</tspan></text> + </g> +</svg> diff --git a/ui/opensnitch/res/icon-red.png b/ui/opensnitch/res/icon-red.png new file mode 100644 index 0000000..d495251 Binary files /dev/null and b/ui/opensnitch/res/icon-red.png differ diff --git a/ui/opensnitch/res/icon-white.png b/ui/opensnitch/res/icon-white.png new file mode 100644 index 0000000..dae1587 Binary files /dev/null and b/ui/opensnitch/res/icon-white.png differ diff --git a/ui/opensnitch/res/icon-white.svg b/ui/opensnitch/res/icon-white.svg new file mode 100644 index 0000000..a0e5a53 --- /dev/null +++ b/ui/opensnitch/res/icon-white.svg @@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + width="512" + height="512" + viewBox="0 0 135.46667 135.46667" + version="1.1" + id="svg8" + inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)" + sodipodi:docname="icon-white.svg" + inkscape:export-filename="/home/ga/Proiektuak/opensnitch/gui/opensnitch/ui/lib/python3.9/site-packages/opensnitch/res/icon-white.png" + inkscape:export-xdpi="96" + inkscape:export-ydpi="96" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:dc="http://purl.org/dc/elements/1.1/"> + <defs + id="defs2" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="0.9899495" + inkscape:cx="288.90363" + inkscape:cy="223.24371" + inkscape:document-units="px" + inkscape:current-layer="g3307" + showgrid="false" + inkscape:window-width="1600" + inkscape:window-height="847" + inkscape:window-x="0" + inkscape:window-y="204" + inkscape:window-maximized="1" + units="px" + inkscape:pagecheckerboard="0" + showguides="true" + inkscape:guide-bbox="true" /> + <metadata + id="metadata5"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Capa 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(0,-284.30001)"> + <g + id="g3307" + transform="matrix(10.756234,0,0,10.756234,-0.76070676,-2776.4763)"> + <path + style="fill:#232629;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.268079px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 2.4759494,290.29453 0.8664313,-2.29552 2.4071989,-0.53291 2.2502482,-1.18347 2.3403932,1.08878 0.37675,2.31748 1.522178,1.17132 0.358073,1.65327 -0.625514,1.50821 -1.245702,0.79112 -8.5513735,0.0878 -1.43716703,-1.05543 -0.29172201,-1.59057 0.73309294,-1.45246 z" + id="path1481" + sodipodi:nodetypes="ccccccccccccccc" + inkscape:export-filename="/home/ga/Proiektuak/opensnitch/gui/opensnitch/ui/lib/python3.9/site-packages/opensnitch/res/icon-white.png" + inkscape:export-xdpi="96" + inkscape:export-ydpi="96" /> + <path + style="fill:#fbffff;fill-opacity:1;stroke:none;stroke-width:0.0245664;stroke-opacity:1" + d="m 2.4221019,295.34968 c -1.0878659,-0.15882 -1.97427807,-0.9635 -2.26698456,-2.05789 -0.05691855,-0.21285 -0.06357126,-0.28265 -0.06357126,-0.66784 0,-0.38518 0.0066228,-0.45498 0.06357126,-0.6678 0.13455437,-0.50309 0.37363404,-0.92281 0.72738412,-1.27693 0.29660044,-0.29693 0.71775704,-0.56133 1.05540334,-0.66262 l 0.094333,-0.0281 0.014889,-0.2737 c 0.065279,-1.20079 0.888671,-2.19921 2.0751826,-2.51629 0.2100531,-0.0562 0.2855258,-0.0637 0.6548195,-0.0645 0.2356099,-5.9e-4 0.4653373,0.0114 0.5274596,0.027 0.1100438,0.0281 0.1100666,0.0281 0.1816696,-0.0629 0.1425139,-0.18079 0.4653264,-0.4742 0.6796678,-0.6177 0.5039623,-0.33746 1.0157321,-0.5065 1.6217201,-0.53562 0.538326,-0.0256 1.0248294,0.0746 1.5082517,0.31109 0.665074,0.32548 1.1364593,0.79739 1.4616053,1.46319 0.233795,0.47874 0.335401,0.96701 0.311946,1.49906 l -0.01218,0.27678 0.199164,0.13128 c 0.109539,0.0722 0.316714,0.24806 0.460388,0.39074 0.813111,0.8075 1.113884,1.93261 0.816781,3.05541 -0.121193,0.45797 -0.457432,1.0356 -0.805977,1.3845 -0.333016,0.33338 -0.91483,0.67934 -1.34009,0.79684 -0.4502688,0.12445 -0.3942394,0.12289 -4.2354147,0.11981 -1.971615,-0.002 -3.6501125,-0.0125 -3.7299933,-0.0242 z m 7.6451481,-0.82834 c 0.545805,-0.1461 1.0314,-0.47864 1.358863,-0.93055 0.13507,-0.18643 0.302726,-0.55751 0.365299,-0.80853 0.07721,-0.30967 0.07737,-0.79285 3.72e-4,-1.10158 -0.185769,-0.74499 -0.759998,-1.39061 -1.459688,-1.64119 -0.139897,-0.0501 -0.212904,-0.0884 -0.205441,-0.1079 0.230936,-0.60245 0.205529,-1.2656 -0.07086,-1.84969 -0.2420436,-0.51155 -0.6116411,-0.88174 -1.1201832,-1.12201 -0.3646743,-0.1723 -0.5814957,-0.2198 -1.0034057,-0.2198 -0.291794,0 -0.3936692,0.0101 -0.5647214,0.0557 -0.6755653,0.17987 -1.2418071,0.63564 -1.5361388,1.23642 l -0.068336,0.13949 -0.1123702,-0.0569 c -0.2090683,-0.10576 -0.5171066,-0.18751 -0.7629941,-0.20252 -0.8518289,-0.052 -1.6804874,0.51274 -1.9528265,1.33076 -0.1542208,0.46323 -0.1386557,0.90554 0.048962,1.39126 0.012582,0.0324 -0.00809,0.0365 -0.1432276,0.0278 -0.1904101,-0.0122 -0.5000142,0.0466 -0.7431816,0.14113 -0.4999194,0.19422 -0.9533668,0.67125 -1.12307682,1.18155 -0.15734055,0.47307 -0.14715788,0.90357 0.032344,1.36764 0.1940906,0.50172 0.6700544,0.95419 1.1841482,1.12568 0.1034943,0.0344 0.2471302,0.0725 0.3191908,0.0848 0.07415,0.0126 1.6976013,0.0205 3.7403281,0.0181 l 3.6093097,-0.004 z m -5.2655768,-1.2975 C 4.2750097,292.90743 3.844102,292.6375 3.844102,292.62395 c 0,-0.0137 0.4309077,-0.28347 0.9575712,-0.59989 l 0.9575723,-0.5752 0.00674,0.39089 0.00674,0.39094 h 1.5707862 1.5707918 v 0.39326 0.39327 H 7.34349 5.772697 l -0.00674,0.39094 -0.00674,0.39093 z m 2.1483996,-2.17014 v -0.39612 H 5.3786739 3.807272 v -0.39326 -0.39328 h 1.570792 1.5707932 l 0.00674,-0.39093 0.00674,-0.39093 0.9575575,0.57524 c 0.5266596,0.3164 0.9606572,0.58453 0.9644402,0.59591 0.00375,0.0115 -0.4050204,0.26764 -0.908454,0.56956 -0.5034315,0.30187 -0.940193,0.56487 -0.970577,0.58441 l -0.055248,0.0354 z" + id="path826" + inkscape:connector-curvature="0" /> + </g> + </g> +</svg> diff --git a/ui/opensnitch/res/icon.png b/ui/opensnitch/res/icon.png new file mode 100644 index 0000000..786e9a0 Binary files /dev/null and b/ui/opensnitch/res/icon.png differ diff --git a/ui/opensnitch/res/preferences.ui b/ui/opensnitch/res/preferences.ui new file mode 100644 index 0000000..bbf8193 --- /dev/null +++ b/ui/opensnitch/res/preferences.ui @@ -0,0 +1,2290 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>PreferencesDialog</class> + <widget class="QDialog" name="PreferencesDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>626</width> + <height>534</height> + </rect> + </property> + <property name="windowTitle"> + <string>Preferences</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="2" column="0"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QPushButton" name="helpButton"> + <property name="mouseTracking"> + <bool>true</bool> + </property> + <property name="toolTip"> + <string/> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="help-browser"> + <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_4"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="cancelButton"> + <property name="text"> + <string>Close</string> + </property> + <property name="icon"> + <iconset theme="window-close"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="applyButton"> + <property name="text"> + <string>Apply</string> + </property> + <property name="icon"> + <iconset theme="document-save"> + <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="acceptButton"> + <property name="text"> + <string>Save</string> + </property> + <property name="icon"> + <iconset theme="emblem-default"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + </widget> + </item> + </layout> + </item> + <item row="0" column="0"> + <widget class="QTabWidget" name="tabWidget"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="tabPosition"> + <enum>QTabWidget::North</enum> + </property> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QWidget" name="tab"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <attribute name="title"> + <string>Pop-ups</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="1" column="0" colspan="3"> + <widget class="QLabel" name="label"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></string> + </property> + <property name="text"> + <string>Default timeout</string> + </property> + </widget> + </item> + <item row="1" column="3"> + <layout class="QHBoxLayout" name="horizontalLayout_4"> + <property name="spacing"> + <number>0</number> + </property> + <item> + <widget class="QPushButton" name="cmdTimeoutUp"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="list-add"> + <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QSpinBox" name="spinUITimeout"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <property name="buttonSymbols"> + <enum>QAbstractSpinBox::NoButtons</enum> + </property> + <property name="accelerated"> + <bool>true</bool> + </property> + <property name="maximum"> + <number>100</number> + </property> + <property name="value"> + <number>30</number> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="cmdTimeoutDown"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="list-remove"> + <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + <item row="0" column="3"> + <widget class="QCheckBox" name="popupsCheck"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="0" column="0" colspan="3"> + <widget class="QLabel" name="label_16"> + <property name="text"> + <string>Disable pop-ups, only display a notification</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="2" column="0" colspan="4"> + <widget class="QToolBox" name="toolBox"> + <widget class="QWidget" name="page_7"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>586</width> + <height>306</height> + </rect> + </property> + <attribute name="label"> + <string>Default options</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_5" rowstretch="0,0,0,0" columnstretch="1,0"> + <item row="1" column="1"> + <widget class="QComboBox" name="comboUIDuration"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <item> + <property name="text"> + <string>once</string> + </property> + </item> + <item> + <property name="text"> + <string>30s</string> + </property> + </item> + <item> + <property name="text"> + <string>5m</string> + </property> + </item> + <item> + <property name="text"> + <string>15m</string> + </property> + </item> + <item> + <property name="text"> + <string>30m</string> + </property> + </item> + <item> + <property name="text"> + <string>1h</string> + </property> + </item> + <item> + <property name="text"> + <string>until reboot</string> + </property> + </item> + <item> + <property name="text"> + <string>forever</string> + </property> + </item> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label_2"> + <property name="toolTip"> + <string><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. the daemon's default action will be applied (see Nodes tab).</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></string> + </property> + <property name="text"> + <string>Action</string> + </property> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>Default position on screen</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_3"> + <property name="toolTip"> + <string>Pop-up default duration</string> + </property> + <property name="text"> + <string>Duration</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QComboBox" name="comboUITarget"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <item> + <property name="text"> + <string>by executable</string> + </property> + </item> + <item> + <property name="text"> + <string>by command line</string> + </property> + </item> + <item> + <property name="text"> + <string>by destination port</string> + </property> + </item> + <item> + <property name="text"> + <string>by destination ip</string> + </property> + </item> + <item> + <property name="text"> + <string>by user id</string> + </property> + </item> + <item> + <property name="text"> + <string>by PID</string> + </property> + </item> + </widget> + </item> + <item row="3" column="1"> + <widget class="QComboBox" name="comboUIDialogPos"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <item> + <property name="text"> + <string>center</string> + </property> + </item> + <item> + <property name="text"> + <string>top right</string> + </property> + </item> + <item> + <property name="text"> + <string>bottom right</string> + </property> + </item> + <item> + <property name="text"> + <string>top left</string> + </property> + </item> + <item> + <property name="text"> + <string>bottom left</string> + </property> + </item> + </widget> + </item> + <item row="0" column="1"> + <widget class="QComboBox" name="comboUIAction"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <item> + <property name="text"> + <string>deny</string> + </property> + <property name="icon"> + <iconset theme="emblem-important"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + </item> + <item> + <property name="text"> + <string>allow</string> + </property> + <property name="icon"> + <iconset theme="emblem-default"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + </item> + <item> + <property name="text"> + <string>reject</string> + </property> + <property name="icon"> + <iconset theme="window-close"> + <normaloff>.</normaloff>.</iconset> + </property> + </item> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string>Default target</string> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="page_8"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>586</width> + <height>306</height> + </rect> + </property> + <attribute name="label"> + <string>More</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_14"> + <item row="2" column="0" colspan="2"> + <layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,0,0"> + <item> + <widget class="QCheckBox" name="uidCheck"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>If checked, this field will be selected when a pop-up is displayed</string> + </property> + <property name="text"> + <string>User ID</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="dstPortCheck"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>If checked, this field will be selected when a pop-up is displayed</string> + </property> + <property name="text"> + <string>Destination port</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="dstIPCheck"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>If checked, this field will be selected when a pop-up is displayed</string> + </property> + <property name="text"> + <string>Destination IP</string> + </property> + </widget> + </item> + </layout> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_18"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></string> + </property> + <property name="text"> + <string>Filter connections also by:</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label_17"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>The advanced view allows you to easily select multiple fields to filter connections</string> + </property> + <property name="text"> + <string>Show advanced view by default</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QCheckBox" name="showAdvancedCheck"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_4"> + <attribute name="title"> + <string>UI</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_6"> + <item row="0" column="0"> + <widget class="QToolBox" name="toolBox_2"> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QWidget" name="page_5"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>586</width> + <height>301</height> + </rect> + </property> + <attribute name="label"> + <string>General</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_16"> + <item row="10" column="2" colspan="2"> + <widget class="QLineEdit" name="lineUIScreenFactor"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Use numbers to define a global scale factor for the whole application: +1, 1.2, 1.5, 2, etc ... + +Use ; to define multiple screens: 1;1.5 etc...</string> + </property> + <property name="placeholderText"> + <string>ex: 1, 1.25, 1.5, 2, ...</string> + </property> + </widget> + </item> + <item row="0" column="2" colspan="2"> + <layout class="QHBoxLayout" name="horizontalLayout_8"> + <property name="spacing"> + <number>0</number> + </property> + <item> + <widget class="QPushButton" name="cmdRefreshUIUp"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="list-add"> + <normaloff>../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QSpinBox" name="spinUIRefresh"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <property name="buttonSymbols"> + <enum>QAbstractSpinBox::NoButtons</enum> + </property> + <property name="accelerated"> + <bool>true</bool> + </property> + <property name="maximum"> + <number>100</number> + </property> + <property name="value"> + <number>1</number> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="cmdRefreshUIDown"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="list-remove"> + <normaloff>../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + <item row="3" column="2" colspan="2"> + <widget class="QComboBox" name="comboUITheme"> + <item> + <property name="text"> + <string>System</string> + </property> + </item> + </widget> + </item> + <item row="12" column="0" colspan="2"> + <widget class="QCheckBox" name="checkAutostart"> + <property name="toolTip"> + <string>By default the GUI is started when login</string> + </property> + <property name="text"> + <string>Autostart the GUI upon login</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="0" column="0" colspan="2"> + <widget class="QLabel" name="label_27"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Refresh interval (seconds)</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="11" column="0" colspan="2"> + <widget class="QLabel" name="labelUIDensity"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Theme density scale</string> + </property> + </widget> + </item> + <item row="7" column="0" colspan="4"> + <widget class="QCheckBox" name="checkUIAutoScreen"> + <property name="text"> + <string>Auto screen scale factor</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="label_21"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Theme</string> + </property> + </widget> + </item> + <item row="1" column="2" colspan="2"> + <widget class="QComboBox" name="comboUILang"/> + </item> + <item row="10" column="0"> + <widget class="QLabel" name="labelUIScreenFactor"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string><html><head/><body><p>Scale factor (use ; for multiple displays) <a href="https://github.com/evilsocket/opensnitch/wiki/GUI-known-problems#gui-size-problems-on-4k-monitors"><span style=" text-decoration: underline; color:#0000ff;">More information</span></a></p></body></html></string> + </property> + </widget> + </item> + <item row="11" column="2" colspan="2"> + <layout class="QHBoxLayout" name="horizontalLayout_9"> + <property name="spacing"> + <number>0</number> + </property> + <item> + <widget class="QPushButton" name="cmdUIDensityUp"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="list-add"> + <normaloff>../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../.designer/backup</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QSpinBox" name="spinUIDensity"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <property name="buttonSymbols"> + <enum>QAbstractSpinBox::NoButtons</enum> + </property> + <property name="accelerated"> + <bool>true</bool> + </property> + <property name="minimum"> + <number>-20</number> + </property> + <property name="maximum"> + <number>20</number> + </property> + <property name="value"> + <number>0</number> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="cmdUIDensityDown"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="list-remove"> + <normaloff>../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../.designer/backup</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + <item row="1" column="0" colspan="2"> + <widget class="QLabel" name="label_19"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Language</string> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="labelThemeError"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item row="5" column="2" colspan="2"> + <widget class="QComboBox" name="comboUIQtPlatform"> + <property name="editable"> + <bool>true</bool> + </property> + <item> + <property name="text"> + <string/> + </property> + </item> + <item> + <property name="text"> + <string notr="true">xcb</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">wayland</string> + </property> + </item> + </widget> + </item> + <item row="5" column="0" colspan="2"> + <widget class="QLabel" name="label_29"> + <property name="toolTip"> + <string>This option will set QT_QPA_PLATFORM when launching the GUI. + +xcb - X11 compatibility. If you experience issues with wayland, use this plugin. +wayland</string> + </property> + <property name="text"> + <string>Qt platform plugin</string> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="page_6"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>259</width> + <height>178</height> + </rect> + </property> + <attribute name="label"> + <string>Server</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_13"> + <item row="0" column="1"> + <widget class="QComboBox" name="comboGrpcMsgSize"> + <property name="editable"> + <bool>true</bool> + </property> + <property name="currentText"> + <string notr="true">4MiB</string> + </property> + <item> + <property name="text"> + <string>4MiB</string> + </property> + </item> + <item> + <property name="text"> + <string>8MiB</string> + </property> + </item> + <item> + <property name="text"> + <string>16MiB</string> + </property> + </item> + <item> + <property name="text"> + <string>32MiB</string> + </property> + </item> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_24"> + <property name="toolTip"> + <string><p>Simple: no authentication</p> +<p>TLS simple/mutual: use SSL certificates to authenticate nodes.</p> +<p>Visit the wiki for more information.</p></string> + </property> + <property name="text"> + <string>Authentication type</string> + </property> + </widget> + </item> + <item row="3" column="0" colspan="2"> + <widget class="QLineEdit" name="lineCertFile"> + <property name="placeholderText"> + <string>Absolute path to the cert file</string> + </property> + </widget> + </item> + <item row="4" column="0" colspan="2"> + <widget class="QLineEdit" name="lineCertKeyFile"> + <property name="placeholderText"> + <string>Absolute path to the cert key file</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QComboBox" name="comboAuthType"> + <item> + <property name="text"> + <string>Simple</string> + </property> + </item> + <item> + <property name="text"> + <string>Simple TLS</string> + </property> + </item> + <item> + <property name="text"> + <string>Mutual TLS</string> + </property> + </item> + </widget> + </item> + <item row="2" column="0" colspan="2"> + <widget class="QLineEdit" name="lineCACertFile"> + <property name="placeholderText"> + <string>Absolute path to the CA cert file</string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label_20"> + <property name="toolTip"> + <string>Maximum size of each message from nodes. Default 4MB</string> + </property> + <property name="text"> + <string>Max gRPC channel size</string> + </property> + </widget> + </item> + <item row="5" column="0" colspan="2"> + <widget class="QLabel" name="label_28"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string><a href="https://github.com/evilsocket/opensnitch/wiki/Nodes-authentication#nodes-authentication-added-in-v161">More information</a></string> + </property> + <property name="openExternalLinks"> + <bool>true</bool> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse</set> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="page_3"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>321</width> + <height>112</height> + </rect> + </property> + <attribute name="label"> + <string>Desktop notifications</string> + </attribute> + <layout class="QFormLayout" name="formLayout"> + <item row="0" column="0" colspan="2"> + <widget class="QGroupBox" name="groupNotifs"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Enable</string> + </property> + <property name="flat"> + <bool>true</bool> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <layout class="QGridLayout" name="gridLayout_9"> + <item row="0" column="0"> + <widget class="QRadioButton" name="radioSysNotifs"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Use system notifications</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QRadioButton" name="radioQtNotifs"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Use Qt notifications</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QLabel" name="labelNotifsWarning"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_5"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="cmdTestNotifs"> + <property name="text"> + <string>Test</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="page_4"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>586</width> + <height>301</height> + </rect> + </property> + <attribute name="label"> + <string>Events tab columns</string> + </attribute> + <layout class="QFormLayout" name="formLayout_2"> + <item row="0" column="0" colspan="2"> + <widget class="QGroupBox" name="groupBox_2"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <layout class="QGridLayout" name="gridLayout_7"> + <property name="leftMargin"> + <number>4</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>4</number> + </property> + <property name="bottomMargin"> + <number>4</number> + </property> + <property name="spacing"> + <number>0</number> + </property> + <item row="2" column="1"> + <widget class="QCheckBox" name="checkHideNode"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Node</string> + </property> + <property name="checked"> + <bool>false</bool> + </property> + </widget> + </item> + <item row="8" column="1"> + <widget class="QCheckBox" name="checkHideCmdline"> + <property name="text"> + <string>Command line</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QCheckBox" name="checkHideTime"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Time</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="2" column="2"> + <widget class="QCheckBox" name="checkHideAction"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Action</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QCheckBox" name="checkHideSrcPort"> + <property name="text"> + <string>Source port</string> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="QCheckBox" name="checkHideSrcIP"> + <property name="text"> + <string>Source IP</string> + </property> + </widget> + </item> + <item row="8" column="2"> + <widget class="QCheckBox" name="checkHideRule"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Rule</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="8" column="0"> + <widget class="QCheckBox" name="checkHideProc"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Process</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="5" column="1"> + <widget class="QCheckBox" name="checkHideDstIP"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Dest IP</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="5" column="0"> + <widget class="QCheckBox" name="checkHideDstPort"> + <property name="text"> + <string>Dest port</string> + </property> + </widget> + </item> + <item row="5" column="2"> + <widget class="QCheckBox" name="checkHideDstHost"> + <property name="text"> + <string>Dest host</string> + </property> + </widget> + </item> + <item row="7" column="0"> + <widget class="QCheckBox" name="checkHideProto"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Protocol</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="7" column="1"> + <widget class="QCheckBox" name="checkHidePID"> + <property name="text"> + <string>PID</string> + </property> + </widget> + </item> + <item row="7" column="2"> + <widget class="QCheckBox" name="checkHideUID"> + <property name="text"> + <string>UID</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_5"> + <attribute name="title"> + <string>Rules</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_11"> + <item row="0" column="0"> + <layout class="QHBoxLayout" name="horizontalLayout_5"> + <item> + <widget class="QCheckBox" name="checkUIRules"> + <property name="toolTip"> + <string>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI. + +Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</string> + </property> + <property name="text"> + <string>Don't save/Delete rules of duration</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="comboUIRules"> + <item> + <property name="text"> + <string>any temporary rules</string> + </property> + </item> + <item> + <property name="text"> + <string>once</string> + </property> + </item> + <item> + <property name="text"> + <string>30s or less</string> + </property> + </item> + <item> + <property name="text"> + <string>5m or less</string> + </property> + </item> + <item> + <property name="text"> + <string>15m or less</string> + </property> + </item> + <item> + <property name="text"> + <string>30m or less</string> + </property> + </item> + <item> + <property name="text"> + <string>1h or less</string> + </property> + </item> + </widget> + </item> + </layout> + </item> + <item row="1" column="0"> + <spacer name="verticalSpacer_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_3"> + <attribute name="title"> + <string>Nodes</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_4" rowstretch="0,0,0,0"> + <item row="0" column="2"> + <widget class="QCheckBox" name="checkApplyToNodes"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Apply configuration to all nodes</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_9"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Ignored"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Version</string> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QComboBox" name="comboNodes"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item row="2" column="2"> + <widget class="QLabel" name="labelNodeVersion"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> + </property> + </widget> + </item> + <item row="0" column="1"> + <spacer name="horizontalSpacer_3"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Preferred</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="2"> + <widget class="QLabel" name="labelNodeName"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> + </property> + </widget> + </item> + <item row="3" column="0" colspan="3"> + <widget class="QToolBox" name="toolBox"> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QWidget" name="page"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>586</width> + <height>260</height> + </rect> + </property> + <attribute name="label"> + <string>General</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_10"> + <item row="1" column="0"> + <widget class="QLabel" name="label_10"> + <property name="toolTip"> + <string><html><head/><body><p>The default action will be applied to new outbound connections in two scenarios:</p><p>when the daemon is not connected to the UI, or when there's a pop-up running.</p></body></html></string> + </property> + <property name="text"> + <string>Default action when the GUI is disconnected</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_11"> + <property name="toolTip"> + <string><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></string> + </property> + <property name="text"> + <string>Default duration</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QComboBox" name="comboNodeDuration"> + <item> + <property name="text"> + <string>once</string> + </property> + </item> + <item> + <property name="text"> + <string>until restart</string> + </property> + </item> + <item> + <property name="text"> + <string>always</string> + </property> + </item> + </widget> + </item> + <item row="1" column="1"> + <widget class="QComboBox" name="comboNodeAction"> + <property name="editable"> + <bool>false</bool> + </property> + <item> + <property name="text"> + <string>deny</string> + </property> + <property name="icon"> + <iconset theme="emblem-important"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + </item> + <item> + <property name="text"> + <string>allow</string> + </property> + <property name="icon"> + <iconset theme="emblem-default"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + </item> + <item> + <property name="text"> + <string>reject</string> + </property> + <property name="icon"> + <iconset theme="window-close"> + <normaloff>.</normaloff>.</iconset> + </property> + </item> + </widget> + </item> + <item row="4" column="1"> + <widget class="QComboBox" name="comboNodeMonitorMethod"> + <property name="accessibleDescription"> + <string notr="true"/> + </property> + <item> + <property name="text"> + <string notr="true">proc</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">ebpf</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">audit</string> + </property> + </item> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="label_12"> + <property name="toolTip"> + <string><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></string> + </property> + <property name="text"> + <string>Debug invalid connections</string> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="label_13"> + <property name="text"> + <string>Process monitor method</string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label_15"> + <property name="toolTip"> + <string><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></string> + </property> + <property name="text"> + <string>Address</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QCheckBox" name="checkInterceptUnknown"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QComboBox" name="comboNodeAddress"> + <property name="editable"> + <bool>true</bool> + </property> + <item> + <property name="text"> + <string>unix:///tmp/osui.sock</string> + </property> + </item> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="page_2"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>376</width> + <height>118</height> + </rect> + </property> + <attribute name="label"> + <string>Logging</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_12"> + <item row="2" column="1"> + <widget class="QCheckBox" name="checkNodeLogUTC"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label_7"> + <property name="toolTip"> + <string><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></string> + </property> + <property name="text"> + <string>Log file</string> + </property> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="label_23"> + <property name="toolTip"> + <string><html><head/><body><p>If checked, OpenSnitch will log timestamp microseconds.</p></body></html></string> + </property> + <property name="text"> + <string>Log timestamp microseconds</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QComboBox" name="comboNodeLogLevel"> + <property name="accessibleDescription"> + <string notr="true"/> + </property> + <item> + <property name="text"> + <string notr="true">DEBUG</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">INFO</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">IMPORTANT</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">WARNING</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">ERROR</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">FATAL</string> + </property> + </item> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_22"> + <property name="toolTip"> + <string><html><head/><body><p>If checked, OpenSnitch will use the UTC timezone for timestamps.</p></body></html></string> + </property> + <property name="text"> + <string>Log UTC timestamps</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_14"> + <property name="text"> + <string>Default log level</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QComboBox" name="comboNodeLogFile"> + <property name="editable"> + <bool>true</bool> + </property> + <item> + <property name="text"> + <string>/var/log/opensnitchd.log</string> + </property> + </item> + <item> + <property name="text"> + <string>/dev/stdout</string> + </property> + </item> + </widget> + </item> + <item row="3" column="1"> + <widget class="QCheckBox" name="checkNodeLogMicro"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="4" column="1"> + <spacer name="verticalSpacer_3"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Maximum</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <widget class="QWidget" name="page_7"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>296</width> + <height>211</height> + </rect> + </property> + <attribute name="label"> + <string>Authentication</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_15"> + <item row="5" column="0"> + <widget class="QLineEdit" name="lineNodeCertFile"> + <property name="placeholderText"> + <string>Absolute path to the cert file</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <layout class="QHBoxLayout" name="horizontalLayout_7"> + <item> + <widget class="QLabel" name="label_25"> + <property name="toolTip"> + <string><p>Simple: no authentication, TLS simple/mutual: use SSL certificates to authenticate nodes.</p><p>Visit the wiki for more information.</p></string> + </property> + <property name="text"> + <string>Authentication type</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="comboNodeAuthType"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <item> + <property name="text"> + <string>Simple</string> + </property> + </item> + <item> + <property name="text"> + <string>Simple TLS</string> + </property> + </item> + <item> + <property name="text"> + <string>Mutual TLS</string> + </property> + </item> + </widget> + </item> + </layout> + </item> + <item row="3" column="0"> + <widget class="QLineEdit" name="lineNodeCACertFile"> + <property name="placeholderText"> + <string>Absolute path to the CA cert file</string> + </property> + </widget> + </item> + <item row="8" column="0"> + <layout class="QHBoxLayout" name="horizontalLayout_6"> + <item> + <widget class="QCheckBox" name="checkNodeAuthSkipVerify"> + <property name="text"> + <string>Don't verify certs</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="comboNodeAuthVerifyType"> + <item> + <property name="text"> + <string>no-client-cert</string> + </property> + </item> + <item> + <property name="text"> + <string>req-cert</string> + </property> + </item> + <item> + <property name="text"> + <string>req-any-cert</string> + </property> + </item> + <item> + <property name="text"> + <string>verify-cert</string> + </property> + </item> + <item> + <property name="text"> + <string>req-and-verify-cert</string> + </property> + </item> + </widget> + </item> + </layout> + </item> + <item row="6" column="0"> + <widget class="QLineEdit" name="lineNodeCertKeyFile"> + <property name="placeholderText"> + <string>Absolute path to the cert key file</string> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QLineEdit" name="lineNodeServerCertFile"> + <property name="placeholderText"> + <string>Absolute path to the server cert file</string> + </property> + </widget> + </item> + <item row="9" column="0"> + <widget class="QLabel" name="label_26"> + <property name="text"> + <string><a href="https://github.com/evilsocket/opensnitch/wiki/Nodes-authentication#nodes-authentication-added-in-v161">More information</a></string> + </property> + <property name="openExternalLinks"> + <bool>true</bool> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + </layout> + </widget> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_8"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>HostName</string> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_2"> + <attribute name="title"> + <string>Database</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_3" rowstretch="0,0,0,0,0,0"> + <property name="sizeConstraint"> + <enum>QLayout::SetDefaultConstraint</enum> + </property> + <property name="leftMargin"> + <number>9</number> + </property> + <property name="verticalSpacing"> + <number>15</number> + </property> + <item row="1" column="1" colspan="3"> + <widget class="QLabel" name="dbLabel"> + <property name="text"> + <string/> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="0" column="3"> + <widget class="QComboBox" name="comboDBType"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <item> + <property name="text"> + <string>In memory</string> + </property> + </item> + <item> + <property name="text"> + <string>File</string> + </property> + </item> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label_6"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Database type</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QPushButton" name="dbFileButton"> + <property name="text"> + <string>Select</string> + </property> + <property name="icon"> + <iconset theme="document-open"> + <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset> + </property> + </widget> + </item> + <item row="5" column="0"> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item row="0" column="1"> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="2" column="0" colspan="4"> + <layout class="QGridLayout" name="gridLayout_8"> + <property name="verticalSpacing"> + <number>10</number> + </property> + <item row="0" column="3"> + <widget class="QSpinBox" name="spinDBMaxDays"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <property name="buttonSymbols"> + <enum>QAbstractSpinBox::NoButtons</enum> + </property> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>99999</number> + </property> + </widget> + </item> + <item row="0" column="2"> + <widget class="QPushButton" name="cmdDBMaxDaysUp"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="list-add"> + <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="1" column="5"> + <widget class="QLabel" name="labelDBPurgeMinutes"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>minutes</string> + </property> + <property name="margin"> + <number>5</number> + </property> + </widget> + </item> + <item row="1" column="3"> + <widget class="QSpinBox" name="spinDBPurgeInterval"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <property name="buttonSymbols"> + <enum>QAbstractSpinBox::NoButtons</enum> + </property> + <property name="minimum"> + <number>5</number> + </property> + <property name="maximum"> + <number>1440</number> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="labelDBPurgeInterval"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Minutes between events purges</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="0" column="1"> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="0" column="5"> + <widget class="QLabel" name="labelDBPurgeDays"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>days</string> + </property> + <property name="margin"> + <number>5</number> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QCheckBox" name="checkDBMaxDays"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Maximum days of events to keep</string> + </property> + </widget> + </item> + <item row="0" column="4"> + <widget class="QPushButton" name="cmdDBMaxDaysDown"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="list-remove"> + <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="1" column="2"> + <widget class="QPushButton" name="cmdDBPurgesUp"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="list-add"> + <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="1" column="4"> + <widget class="QPushButton" name="cmdDBPurgesDown"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="list-remove"> + <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QCheckBox" name="checkDBJrnlWal"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Enable DB Write-Ahead Logging (WAL)</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="statusLabel"> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/ui/opensnitch/res/process_details.ui b/ui/opensnitch/res/process_details.ui new file mode 100644 index 0000000..5c34de5 --- /dev/null +++ b/ui/opensnitch/res/process_details.ui @@ -0,0 +1,269 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ProcessDetailsDialog</class> + <widget class="QDialog" name="ProcessDetailsDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>731</width> + <height>478</height> + </rect> + </property> + <property name="windowTitle"> + <string>Process details</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="labelProcIcon"> + <property name="minimumSize"> + <size> + <width>48</width> + <height>48</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>64</width> + <height>64</height> + </size> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QLabel" name="labelProcName"> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="text"> + <string>loading...</string> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="labelProcArgs"> + <property name="text"> + <string>loading...</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </item> + <item> + <widget class="QLabel" name="labelCwd"> + <property name="text"> + <string>CWD: loading...</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QLabel" name="labelStatm"> + <property name="text"> + <string>mem stats: loading...</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </item> + <item> + <widget class="Line" name="line"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <widget class="QTabWidget" name="tabWidget"> + <property name="tabPosition"> + <enum>QTabWidget::South</enum> + </property> + <property name="currentIndex"> + <number>0</number> + </property> + <property name="documentMode"> + <bool>true</bool> + </property> + <widget class="QWidget" name="tab_2"> + <attribute name="title"> + <string>Status</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_3"> + <item row="0" column="1"> + <widget class="QPlainTextEdit" name="textStatus"> + <property name="undoRedoEnabled"> + <bool>false</bool> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_3"> + <attribute name="title"> + <string>Open files</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_4"> + <item row="1" column="0"> + <widget class="QPlainTextEdit" name="textOpenedFiles"> + <property name="undoRedoEnabled"> + <bool>false</bool> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="tabWidgetPage1"> + <attribute name="title"> + <string>I/O Statistics</string> + </attribute> + <layout class="QGridLayout" name="gridLayout"> + <item row="1" column="0"> + <widget class="QPlainTextEdit" name="textIOStats"> + <property name="undoRedoEnabled"> + <bool>false</bool> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="tabWidgetPage2"> + <attribute name="title"> + <string>Memory mapped files</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="1" column="0"> + <widget class="QPlainTextEdit" name="textMappedFiles"> + <property name="undoRedoEnabled"> + <bool>false</bool> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab"> + <attribute name="title"> + <string>Stack</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_5"> + <item row="0" column="0"> + <widget class="QPlainTextEdit" name="textStack"> + <property name="undoRedoEnabled"> + <bool>false</bool> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_4"> + <attribute name="title"> + <string>Environment variables</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_6"> + <item row="0" column="0"> + <widget class="QPlainTextEdit" name="textEnv"> + <property name="undoRedoEnabled"> + <bool>false</bool> + </property> + </widget> + </item> + </layout> + </widget> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Application pids</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="comboPids"> + <property name="maxVisibleItems"> + <number>100</number> + </property> + <property name="sizeAdjustPolicy"> + <enum>QComboBox::AdjustToContents</enum> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_3"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="cmdAction"> + <property name="toolTip"> + <string>Start or stop monitoring this process</string> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="media-playback-start"/> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="cmdClose"> + <property name="text"> + <string>Close</string> + </property> + <property name="icon"> + <iconset theme="window-close"/> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/ui/opensnitch/res/prompt.ui b/ui/opensnitch/res/prompt.ui new file mode 100644 index 0000000..2a132c0 --- /dev/null +++ b/ui/opensnitch/res/prompt.ui @@ -0,0 +1,914 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Dialog</class> + <widget class="QDialog" name="Dialog"> + <property name="windowModality"> + <enum>Qt::NonModal</enum> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>515</width> + <height>315</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>200</height> + </size> + </property> + <property name="font"> + <font> + <kerning>true</kerning> + </font> + </property> + <property name="windowTitle"> + <string>opensnitch-qt</string> + </property> + <property name="windowIcon"> + <iconset resource="resources.qrc"> + <normaloff>:/pics/icon-white.png</normaloff> + <normalon>:/pics/icon.png</normalon>:/pics/icon-white.png</iconset> + </property> + <property name="sizeGripEnabled"> + <bool>false</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,0,0,0,0,1"> + <property name="spacing"> + <number>2</number> + </property> + <property name="sizeConstraint"> + <enum>QLayout::SetMinAndMaxSize</enum> + </property> + <property name="leftMargin"> + <number>5</number> + </property> + <property name="topMargin"> + <number>5</number> + </property> + <property name="rightMargin"> + <number>5</number> + </property> + <property name="bottomMargin"> + <number>5</number> + </property> + <item> + <layout class="QFormLayout" name="formLayout"> + <property name="fieldGrowthPolicy"> + <enum>QFormLayout::ExpandingFieldsGrow</enum> + </property> + <property name="labelAlignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> + </property> + <property name="verticalSpacing"> + <number>0</number> + </property> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLabel" name="iconLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>64</width> + <height>64</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>64</width> + <height>64</height> + </size> + </property> + <property name="text"> + <string/> + </property> + <property name="pixmap"> + <pixmap resource="resources.qrc">:/pics/icon.png</pixmap> + </property> + <property name="scaledContents"> + <bool>true</bool> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </item> + <item row="0" column="1"> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <property name="spacing"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="appNameLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>16</pointsize> + <weight>75</weight> + <bold>true</bold> + <kerning>true</kerning> + </font> + </property> + <property name="text"> + <string notr="true">Chromium Web Browser</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + <property name="textInteractionFlags"> + <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="appDescriptionLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>10</pointsize> + <weight>50</weight> + <italic>true</italic> + <bold>false</bold> + <underline>true</underline> + <kerning>true</kerning> + </font> + </property> + <property name="text"> + <string notr="true"><html><head/><body><p>/opt/google/chrome/bin/chrome --something abc --more-long def --for-word-wrapping</p></body></html></string> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + <property name="textInteractionFlags"> + <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="appPathLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string notr="true">(/path/to/bin/chromium)</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + <property name="textInteractionFlags"> + <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="argsLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <family>DejaVu Sans</family> + <pointsize>9</pointsize> + <kerning>true</kerning> + </font> + </property> + <property name="text"> + <string notr="true">(/path/to/bin/chromium)</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + <property name="textInteractionFlags"> + <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </item> + <item> + <widget class="Line" name="line"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <layout class="QVBoxLayout" name="verticalLayout_5"> + <property name="spacing"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="messageLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string notr="true">Chromium Web Browser wants to connect to www.evilsocket.net on tcp port 443. And maybe to www.goodsocket.net on port 344</string> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + <property name="margin"> + <number>2</number> + </property> + <property name="textInteractionFlags"> + <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QGridLayout" name="gridLayout"> + <property name="topMargin"> + <number>6</number> + </property> + <property name="verticalSpacing"> + <number>3</number> + </property> + <item row="1" column="1"> + <widget class="QLabel" name="label"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>10</pointsize> + <weight>75</weight> + <bold>true</bold> + <kerning>true</kerning> + </font> + </property> + <property name="text"> + <string>Source IP</string> + </property> + </widget> + </item> + <item row="0" column="3"> + <widget class="QLabel" name="cwdLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>10</pointsize> + <kerning>true</kerning> + </font> + </property> + <property name="text"> + <string/> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="textInteractionFlags"> + <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item row="7" column="3"> + <widget class="QLabel" name="pidLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>10</pointsize> + <kerning>true</kerning> + </font> + </property> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="text"> + <string>TextLabel</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="textInteractionFlags"> + <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item row="6" column="1"> + <widget class="QLabel" name="label_6"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>10</pointsize> + <weight>75</weight> + <bold>true</bold> + <kerning>true</kerning> + </font> + </property> + <property name="text"> + <string>User ID</string> + </property> + </widget> + </item> + <item row="6" column="0"> + <widget class="QCheckBox" name="checkUserID"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="7" column="1"> + <widget class="QLabel" name="pidLabelWidget"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>10</pointsize> + <weight>75</weight> + <bold>true</bold> + <kerning>true</kerning> + </font> + </property> + <property name="text"> + <string>Process ID</string> + </property> + </widget> + </item> + <item row="5" column="1"> + <widget class="QLabel" name="destPortLabel_1"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>10</pointsize> + <weight>75</weight> + <bold>true</bold> + <kerning>true</kerning> + </font> + </property> + <property name="text"> + <string>Dst Port</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLabel" name="label_3"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>10</pointsize> + <weight>75</weight> + <bold>true</bold> + <kerning>true</kerning> + </font> + </property> + <property name="text"> + <string>Destination IP</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLabel" name="label_2"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>10</pointsize> + <weight>75</weight> + <bold>true</bold> + <kerning>true</kerning> + </font> + </property> + <property name="text"> + <string><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></string> + </property> + </widget> + </item> + <item row="6" column="3"> + <widget class="QLabel" name="uidLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>10</pointsize> + <kerning>true</kerning> + </font> + </property> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="text"> + <string>TextLabel</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="textInteractionFlags"> + <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item row="2" column="3"> + <widget class="QLabel" name="destIPLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>10</pointsize> + <kerning>true</kerning> + </font> + </property> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="text"> + <string>TextLabel</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="textInteractionFlags"> + <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item row="0" column="2"> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="2" column="0"> + <widget class="QCheckBox" name="checkDstIP"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="2" column="2"> + <widget class="QComboBox" name="whatIPCombo"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumContentsLength"> + <number>0</number> + </property> + </widget> + </item> + <item row="5" column="0"> + <widget class="QCheckBox" name="checkDstPort"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="5" column="3"> + <widget class="QLabel" name="destPortLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>10</pointsize> + <kerning>true</kerning> + </font> + </property> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="text"> + <string>TextLabel</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="textInteractionFlags"> + <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item row="1" column="3"> + <widget class="QLabel" name="sourceIPLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>10</pointsize> + <kerning>true</kerning> + </font> + </property> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="text"> + <string>TextLabel</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="textInteractionFlags"> + <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QLabel" name="label_5"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string notr="true"/> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout" stretch="3,2,1,0,0"> + <property name="sizeConstraint"> + <enum>QLayout::SetMinimumSize</enum> + </property> + <item> + <widget class="QComboBox" name="whatCombo"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>97</width> + <height>26</height> + </size> + </property> + <item> + <property name="text"> + <string>from this executable</string> + </property> + </item> + <item> + <property name="text"> + <string>from this command line</string> + </property> + </item> + <item> + <property name="text"> + <string>this destination port</string> + </property> + </item> + <item> + <property name="text"> + <string>this user</string> + </property> + </item> + <item> + <property name="text"> + <string>this destination ip</string> + </property> + </item> + <item> + <property name="text"> + <string>from this PID</string> + </property> + </item> + </widget> + </item> + <item> + <widget class="QComboBox" name="durationCombo"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>97</width> + <height>26</height> + </size> + </property> + <property name="currentText"> + <string>once</string> + </property> + <item> + <property name="text"> + <string>once</string> + </property> + </item> + <item> + <property name="text"> + <string>30s</string> + </property> + </item> + <item> + <property name="text"> + <string>5m</string> + </property> + </item> + <item> + <property name="text"> + <string>15m</string> + </property> + </item> + <item> + <property name="text"> + <string>30m</string> + </property> + </item> + <item> + <property name="text"> + <string>1h</string> + </property> + </item> + <item> + <property name="text"> + <string>until reboot</string> + </property> + </item> + <item> + <property name="text"> + <string>forever</string> + </property> + </item> + </widget> + </item> + <item> + <widget class="QToolButton" name="actionButton"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>97</width> + <height>26</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true"/> + </property> + <property name="text"> + <string>action</string> + </property> + <property name="icon"> + <iconset theme="emblem-important"> + <normaloff>.</normaloff>.</iconset> + </property> + <property name="popupMode"> + <enum>QToolButton::MenuButtonPopup</enum> + </property> + <property name="toolButtonStyle"> + <enum>Qt::ToolButtonTextBesideIcon</enum> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="allowButton"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>97</width> + <height>26</height> + </size> + </property> + <property name="text"> + <string>Allow</string> + </property> + <property name="icon"> + <iconset theme="emblem-default"> + <normaloff>.</normaloff>.</iconset> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="checkAdvanced"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>26</width> + <height>26</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>40</width> + <height>30</height> + </size> + </property> + <property name="text"> + <string>+</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="checked"> + <bool>false</bool> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <tabstops> + <tabstop>checkAdvanced</tabstop> + <tabstop>allowButton</tabstop> + <tabstop>actionButton</tabstop> + <tabstop>durationCombo</tabstop> + <tabstop>whatCombo</tabstop> + <tabstop>checkDstPort</tabstop> + <tabstop>checkDstIP</tabstop> + <tabstop>checkUserID</tabstop> + <tabstop>whatIPCombo</tabstop> + </tabstops> + <resources> + <include location="resources.qrc"/> + <include location="../../../../../../../../.designer/backup/resources.qrc"/> + </resources> + <connections/> +</ui> diff --git a/ui/opensnitch/res/resources.qrc b/ui/opensnitch/res/resources.qrc new file mode 100644 index 0000000..35bde88 --- /dev/null +++ b/ui/opensnitch/res/resources.qrc @@ -0,0 +1,8 @@ +<RCC> + <qresource prefix="pics"> + <file>icon-white.svg</file> + <file>icon-white.png</file> + <file>icon-red.png</file> + <file>icon.png</file> + </qresource> +</RCC> diff --git a/ui/opensnitch/res/ruleseditor.ui b/ui/opensnitch/res/ruleseditor.ui new file mode 100644 index 0000000..3100376 --- /dev/null +++ b/ui/opensnitch/res/ruleseditor.ui @@ -0,0 +1,1153 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>RulesDialog</class> + <widget class="QDialog" name="RulesDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>552</width> + <height>559</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="windowTitle"> + <string>Rule</string> + </property> + <layout class="QGridLayout" name="gridLayout_4"> + <item row="6" column="0"> + <widget class="QGroupBox" name="enableGroup"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="flat"> + <bool>false</bool> + </property> + <property name="checkable"> + <bool>false</bool> + </property> + <property name="checked"> + <bool>false</bool> + </property> + <layout class="QGridLayout" name="gridLayout" columnstretch="0,0,0,0,0,0,0"> + <property name="sizeConstraint"> + <enum>QLayout::SetDefaultConstraint</enum> + </property> + <property name="topMargin"> + <number>4</number> + </property> + <property name="bottomMargin"> + <number>4</number> + </property> + <property name="verticalSpacing"> + <number>12</number> + </property> + <item row="3" column="1"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Action</string> + </property> + </widget> + </item> + <item row="4" column="4"> + <spacer name="horizontalSpacer_5"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="3" column="4"> + <spacer name="horizontalSpacer_4"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="4" column="1"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>Duration</string> + </property> + </widget> + </item> + <item row="4" column="6"> + <widget class="QComboBox" name="durationCombo"> + <item> + <property name="text"> + <string>once</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">30s</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">5m</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">15m</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">30m</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">1h</string> + </property> + </item> + <item> + <property name="text"> + <string>until reboot</string> + </property> + </item> + <item> + <property name="text"> + <string>always</string> + </property> + </item> + </widget> + </item> + <item row="3" column="6"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QRadioButton" name="actionDenyRadio"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Deny will just discard the connection</string> + </property> + <property name="text"> + <string>Deny</string> + </property> + <property name="icon"> + <iconset theme="emblem-important"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="actionRejectRadio"> + <property name="toolTip"> + <string>Reject will drop the connection, and kill the socket that initiated it</string> + </property> + <property name="text"> + <string>Reject</string> + </property> + <property name="icon"> + <iconset theme="window-close"> + <normaloff>.</normaloff>.</iconset> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="actionAllowRadio"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Allow will allow the connection</string> + </property> + <property name="layoutDirection"> + <enum>Qt::LeftToRight</enum> + </property> + <property name="text"> + <string>Allow</string> + </property> + <property name="icon"> + <iconset theme="emblem-default"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item row="4" column="0"> + <widget class="QCheckBox" name="enableCheck"> + <property name="text"> + <string>Enable</string> + </property> + </widget> + </item> + <item row="5" column="0"> + <widget class="QCheckBox" name="precedenceCheck"> + <property name="toolTip"> + <string>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one. + +You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example: + +[x] Priority - 000-priority-rule +[ ] Priority - 001-less-priority-rule</string> + </property> + <property name="text"> + <string>Priority rule</string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLineEdit" name="ruleNameEdit"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them. + +000-allow-localhost +001-deny-broadcast +...</string> + </property> + <property name="placeholderText"> + <string>Name</string> + </property> + <property name="clearButtonEnabled"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="8" column="0"> + <widget class="QLabel" name="statusLabel"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string/> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="10" column="0"> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <spacer name="horizontalSpacer_6"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Apply|QDialogButtonBox::Close|QDialogButtonBox::Help|QDialogButtonBox::Reset</set> + </property> + </widget> + </item> + </layout> + </item> + <item row="2" column="0"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Node</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="nodesCombo"/> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QCheckBox" name="nodeApplyAllCheck"> + <property name="text"> + <string>Apply rule to all nodes</string> + </property> + </widget> + </item> + </layout> + </item> + <item row="7" column="0"> + <widget class="QTabWidget" name="tabWidget"> + <property name="currentIndex"> + <number>0</number> + </property> + <property name="elideMode"> + <enum>Qt::ElideRight</enum> + </property> + <property name="documentMode"> + <bool>true</bool> + </property> + <widget class="QWidget" name="tabWidgetPage1"> + <attribute name="icon"> + <iconset theme="system-run"> + <normaloff>.</normaloff>.</iconset> + </attribute> + <attribute name="title"> + <string>Applications</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="0" column="1" colspan="2"> + <widget class="QLineEdit" name="procLine"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="toolTip"> + <string><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></string> + </property> + <property name="placeholderText"> + <string notr="true">/path/to/executable, .*/bin/executable[0-9\.]+$, ...</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QCheckBox" name="checkCmdlineRegexp"> + <property name="text"> + <string>Is regular expression</string> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QCheckBox" name="uidCheck"> + <property name="text"> + <string>From this user ID</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QCheckBox" name="cmdlineCheck"> + <property name="text"> + <string>From this command line</string> + </property> + </widget> + </item> + <item row="2" column="1" colspan="2"> + <widget class="QLineEdit" name="cmdlineLine"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="toolTip"> + <string><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></string> + </property> + <property name="placeholderText"> + <string notr="true">curl -L https://www.domain.com</string> + </property> + </widget> + </item> + <item row="5" column="0"> + <widget class="QCheckBox" name="pidCheck"> + <property name="text"> + <string>From this PID</string> + </property> + </widget> + </item> + <item row="5" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout_11"> + <item> + <spacer name="horizontalSpacer_11"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLineEdit" name="pidLine"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + </layout> + </item> + <item row="4" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout_8"> + <item> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::MinimumExpanding</enum> + </property> + </spacer> + </item> + <item> + <widget class="QComboBox" name="uidCombo"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="editable"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + <item row="0" column="0"> + <widget class="QCheckBox" name="procCheck"> + <property name="text"> + <string>From this executable</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QCheckBox" name="checkProcRegexp"> + <property name="text"> + <string>is regular expression</string> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="tabWidgetPage2"> + <attribute name="icon"> + <iconset theme="preferences-system-network"> + <normaloff>.</normaloff>.</iconset> + </attribute> + <attribute name="title"> + <string>Network</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_3"> + <item row="6" column="1"> + <spacer name="horizontalSpacer_9"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Maximum</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>60</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="7" column="4" colspan="2"> + <widget class="QComboBox" name="ifaceCombo"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="editable"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="0" column="5"> + <widget class="QComboBox" name="protoCombo"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="toolTip"> + <string><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></string> + </property> + <property name="editable"> + <bool>true</bool> + </property> + <property name="currentText"> + <string>TCP</string> + </property> + <item> + <property name="text"> + <string notr="true">TCP</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">UDP</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">UDPLITE</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">TCP6</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">UDP6</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">UDPLITE6</string> + </property> + </item> + <item> + <property name="text"> + <string>ICMP</string> + </property> + </item> + <item> + <property name="text"> + <string>ICMP6</string> + </property> + </item> + <item> + <property name="text"> + <string>SCTP</string> + </property> + </item> + <item> + <property name="text"> + <string>SCTP6</string> + </property> + </item> + </widget> + </item> + <item row="5" column="2" colspan="4"> + <widget class="QLineEdit" name="dstHostLine"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="toolTip"> + <string>Commas or spaces are not allowed to specify multiple domains. + +Use regular expressions instead: +.*(opensnitch|duckduckgo).com +.*\.google.com + +or a single domain: +www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ... +gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</string> + </property> + <property name="placeholderText"> + <string>www.domain.org, .*\.domain.org</string> + </property> + </widget> + </item> + <item row="6" column="0"> + <widget class="QCheckBox" name="dstIPCheck"> + <property name="text"> + <string>To this IP / Network</string> + </property> + </widget> + </item> + <item row="5" column="1"> + <spacer name="horizontalSpacer_10"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Maximum</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>60</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="0" column="0"> + <widget class="QCheckBox" name="protoCheck"> + <property name="text"> + <string>Protocol</string> + </property> + </widget> + </item> + <item row="4" column="2" colspan="4"> + <widget class="QComboBox" name="srcIPCombo"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="toolTip"> + <string>You can specify a single IP: +- 192.168.1.1 + +or a regular expression: +- 192\.168\.1\.[0-9]+ + +multiple IPs: +- ^(192\.168\.1\.1|172\.16\.0\.1)$ + +You can also specify a subnet: +- 192.168.1.0/24 + +Note: Commas or spaces are not allowed to separate IPs or networks.</string> + </property> + <property name="editable"> + <bool>true</bool> + </property> + <property name="currentText"> + <string notr="true">LAN</string> + </property> + <item> + <property name="text"> + <string>LAN</string> + </property> + </item> + <item> + <property name="text"> + <string>MULTICAST</string> + </property> + </item> + <item> + <property name="text"> + <string>127.0.0.0/8</string> + </property> + </item> + <item> + <property name="text"> + <string>192.168.0.0/24</string> + </property> + </item> + <item> + <property name="text"> + <string>192.168.1.0/24</string> + </property> + </item> + <item> + <property name="text"> + <string>192.168.2.0/24</string> + </property> + </item> + <item> + <property name="text"> + <string>192.168.0.0/16</string> + </property> + </item> + <item> + <property name="text"> + <string>169.254.0.0/16</string> + </property> + </item> + <item> + <property name="text"> + <string>172.16.0.0/12</string> + </property> + </item> + <item> + <property name="text"> + <string>10.0.0.0/8</string> + </property> + </item> + <item> + <property name="text"> + <string>::1/128</string> + </property> + </item> + <item> + <property name="text"> + <string>fc00::/7</string> + </property> + </item> + <item> + <property name="text"> + <string>ff00::/8</string> + </property> + </item> + <item> + <property name="text"> + <string>fe80::/10</string> + </property> + </item> + <item> + <property name="text"> + <string>fd00::/8</string> + </property> + </item> + </widget> + </item> + <item row="4" column="0"> + <widget class="QCheckBox" name="srcIPCheck"> + <property name="text"> + <string>From this IP / Network</string> + </property> + </widget> + </item> + <item row="5" column="0"> + <widget class="QCheckBox" name="dstHostCheck"> + <property name="text"> + <string>To this host</string> + </property> + </widget> + </item> + <item row="6" column="2" colspan="4"> + <widget class="QComboBox" name="dstIPCombo"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="toolTip"> + <string>You can specify a single IP: +- 192.168.1.1 + +or a regular expression: +- 192\.168\.1\.[0-9]+ + +multiple IPs: +- ^(192\.168\.1\.1|172\.16\.0\.1)$ + +You can also specify a subnet: +- 192.168.1.0/24 + +Note: Commas or spaces are not allowed to separate IPs or networks.</string> + </property> + <property name="statusTip"> + <string/> + </property> + <property name="editable"> + <bool>true</bool> + </property> + <property name="currentText"> + <string notr="true">LAN</string> + </property> + <item> + <property name="text"> + <string notr="true">LAN</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">MULTICAST</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">127.0.0.0/8</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">192.168.0.0/24</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">192.168.1.0/24</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">192.168.2.0/24</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">192.168.0.0/16</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">169.254.0.0/16</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">172.16.0.0/12</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">10.0.0.0/8</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">::1/128</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">fc00::/7</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">ff00::/8</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">fe80::/10</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">fd00::/8</string> + </property> + </item> + </widget> + </item> + <item row="7" column="0"> + <widget class="QCheckBox" name="ifaceCheck"> + <property name="text"> + <string>Network interface</string> + </property> + </widget> + </item> + <item row="1" column="0" colspan="6"> + <layout class="QHBoxLayout" name="horizontalLayout_4"> + <item> + <widget class="QCheckBox" name="srcPortCheck"> + <property name="text"> + <string>From this port</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="srcPortLine"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="toolTip"> + <string><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_3"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QCheckBox" name="dstPortCheck"> + <property name="text"> + <string>To this port</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="dstPortLine"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <widget class="QWidget" name="tabWidgetPage3"> + <attribute name="icon"> + <iconset theme="document-properties"> + <normaloff>.</normaloff>.</iconset> + </attribute> + <attribute name="title"> + <string>List of domains/IPs</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_5"> + <item row="3" column="0"> + <widget class="QCheckBox" name="dstListNetsCheck"> + <property name="text"> + <string>To this list of network ranges</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QCheckBox" name="dstListIPsCheck"> + <property name="text"> + <string>To this list of IPs</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout_7"> + <item> + <widget class="QPushButton" name="selectIPsListButton"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="system-search"> + <normaloff>.</normaloff>.</iconset> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="dstListIPsLine"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="toolTip"> + <string><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></string> + </property> + </widget> + </item> + </layout> + </item> + <item row="0" column="0"> + <widget class="QCheckBox" name="dstListsCheck"> + <property name="text"> + <string>To this list of domains</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout_9"> + <item> + <widget class="QPushButton" name="selectNetsListButton"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="system-search"> + <normaloff>.</normaloff>.</iconset> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="dstListNetsLine"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="toolTip"> + <string><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></string> + </property> + </widget> + </item> + </layout> + </item> + <item row="0" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout_6"> + <item> + <widget class="QPushButton" name="selectListButton"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="system-search"> + <normaloff>.</normaloff>.</iconset> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="dstListsLine"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="toolTip"> + <string><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></string> + </property> + </widget> + </item> + </layout> + </item> + <item row="1" column="0"> + <widget class="QCheckBox" name="dstListRegexpCheck"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>To this list of domains +(regular expressions)</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout_10"> + <item> + <widget class="QPushButton" name="selectListRegexpButton"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="system-search"> + <normaloff>.</normaloff>.</iconset> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="dstRegexpListsLine"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="toolTip"> + <string><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab"> + <attribute name="title"> + <string>More</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_6"> + <item row="0" column="0"> + <widget class="QCheckBox" name="sensitiveCheck"> + <property name="toolTip"> + <string><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></string> + </property> + <property name="text"> + <string>Case-sensitive</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QCheckBox" name="nologCheck"> + <property name="statusTip"> + <string>Don't log connections that match this rule</string> + </property> + <property name="text"> + <string>Don't log connections</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Maximum</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </widget> + </item> + <item row="9" column="0"> + <widget class="Line" name="line"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QPlainTextEdit" name="ruleDescEdit"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>16777215</width> + <height>60</height> + </size> + </property> + <property name="placeholderText"> + <string>Description...</string> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/ui/opensnitch/res/stats.ui b/ui/opensnitch/res/stats.ui new file mode 100644 index 0000000..08c6294 --- /dev/null +++ b/ui/opensnitch/res/stats.ui @@ -0,0 +1,1844 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>StatsDialog</class> + <widget class="QDialog" name="StatsDialog"> + <property name="windowModality"> + <enum>Qt::NonModal</enum> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>863</width> + <height>597</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>300</width> + <height>220</height> + </size> + </property> + <property name="font"> + <font> + <kerning>true</kerning> + </font> + </property> + <property name="windowTitle"> + <string>OpenSnitch Network Statistics</string> + </property> + <property name="windowIcon"> + <iconset resource="resources.qrc"> + <normaloff>:/pics/icon-white.svg</normaloff> + <normalon>:/pics/icon-white.svg</normalon> + <disabledoff>:/pics/icon-white.svg</disabledoff> + <disabledon>:/pics/icon-white.svg</disabledon> + <activeoff>:/pics/icon-white.svg</activeoff> + <activeon>:/pics/icon-white.svg</activeon>:/pics/icon-white.svg</iconset> + </property> + <property name="sizeGripEnabled"> + <bool>false</bool> + </property> + <property name="modal"> + <bool>false</bool> + </property> + <layout class="QGridLayout" name="gridLayout_4"> + <property name="leftMargin"> + <number>4</number> + </property> + <property name="topMargin"> + <number>2</number> + </property> + <property name="rightMargin"> + <number>4</number> + </property> + <property name="bottomMargin"> + <number>4</number> + </property> + <property name="horizontalSpacing"> + <number>2</number> + </property> + <property name="verticalSpacing"> + <number>4</number> + </property> + <item row="3" column="0"> + <widget class="QFrame" name="navToolBar"> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_17"> + <property name="leftMargin"> + <number>4</number> + </property> + <property name="topMargin"> + <number>2</number> + </property> + <property name="rightMargin"> + <number>4</number> + </property> + <property name="bottomMargin"> + <number>4</number> + </property> + <item> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>Filter</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="comboAction"> + <item> + <property name="text"> + <string>-</string> + </property> + </item> + <item> + <property name="text"> + <string>Allow</string> + </property> + <property name="icon"> + <iconset theme="emblem-default"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + </item> + <item> + <property name="text"> + <string>Deny</string> + </property> + <property name="icon"> + <iconset theme="emblem-important"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + </item> + <item> + <property name="text"> + <string>Reject</string> + </property> + <property name="icon"> + <iconset theme="window-close"> + <normaloff>../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + </item> + </widget> + </item> + <item> + <widget class="QLineEdit" name="filterLine"> + <property name="text"> + <string notr="true"/> + </property> + <property name="frame"> + <bool>true</bool> + </property> + <property name="placeholderText"> + <string>Ex.: firefox</string> + </property> + <property name="clearButtonEnabled"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_3"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Minimum</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="prevButton"> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="go-previous"> + <normaloff>../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="labelRowsCount"> + <property name="text"> + <string>0</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="nextButton"> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="go-next"> + <normaloff>../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="limitCombo"> + <property name="currentIndex"> + <number>0</number> + </property> + <property name="sizeAdjustPolicy"> + <enum>QComboBox::AdjustToContents</enum> + </property> + <item> + <property name="text"> + <string>50</string> + </property> + </item> + <item> + <property name="text"> + <string>100</string> + </property> + </item> + <item> + <property name="text"> + <string>200</string> + </property> + </item> + <item> + <property name="text"> + <string>300</string> + </property> + </item> + <item> + <property name="text"> + <string/> + </property> + </item> + </widget> + </item> + <item> + <widget class="QPushButton" name="cmdCleanSql"> + <property name="toolTip"> + <string>Delete all intercepted events</string> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="edit-clear-all"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="helpButton"> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="help-browser"> + <normaloff>../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="0" column="0"> + <widget class="QFrame" name="frame"> + <property name="frameShape"> + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + <layout class="QGridLayout" name="gridLayout"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <property name="spacing"> + <number>0</number> + </property> + <item row="0" column="0"> + <layout class="QHBoxLayout" name="horizontalLayout_10"> + <item> + <widget class="QPushButton" name="actionsButton"> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="format-justify-fill"> + <normaloff>.</normaloff>.</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="prefsButton"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="preferences-system"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="newRuleButton"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>32</horstretch> + <verstretch>32</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Create a new rule</string> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="document-new"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="fwButton"> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="security-high"/> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_9"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>60</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="nodeLabel"> + <property name="text"> + <string><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></string> + </property> + <property name="margin"> + <number>5</number> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="label_2"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>11</pointsize> + <weight>75</weight> + <bold>true</bold> + <kerning>true</kerning> + </font> + </property> + <property name="text"> + <string>Status</string> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> + </property> + <property name="margin"> + <number>5</number> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="statusLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>11</pointsize> + <kerning>true</kerning> + </font> + </property> + <property name="text"> + <string>-</string> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> + </property> + <property name="margin"> + <number>5</number> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="startButton"> + <property name="toolTip"> + <string>Start or Stop interception</string> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="media-playback-start"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="checked"> + <bool>false</bool> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item row="1" column="0"> + <widget class="QTabWidget" name="tabWidget"> + <property name="tabPosition"> + <enum>QTabWidget::North</enum> + </property> + <property name="tabShape"> + <enum>QTabWidget::Rounded</enum> + </property> + <property name="currentIndex"> + <number>0</number> + </property> + <property name="documentMode"> + <bool>true</bool> + </property> + <widget class="QWidget" name="tab"> + <attribute name="icon"> + <iconset theme="view-sort-ascending"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </attribute> + <attribute name="title"> + <string>Events</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_8"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>4</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_16"> + <property name="spacing"> + <number>0</number> + </property> + <item> + <widget class="GenericTableView" name="eventsTable"> + <property name="styleSheet"> + <string notr="true"/> + </property> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Plain</enum> + </property> + <property name="autoScroll"> + <bool>false</bool> + </property> + <property name="editTriggers"> + <set>QAbstractItemView::NoEditTriggers</set> + </property> + <property name="selectionBehavior"> + <enum>QAbstractItemView::SelectRows</enum> + </property> + <property name="horizontalScrollMode"> + <enum>QAbstractItemView::ScrollPerPixel</enum> + </property> + <property name="showGrid"> + <bool>false</bool> + </property> + <attribute name="verticalHeaderVisible"> + <bool>false</bool> + </attribute> + </widget> + </item> + <item> + <widget class="QScrollBar" name="connectionsTableScrollBar"> + <property name="styleSheet"> + <string notr="true"/> + </property> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_8"> + <attribute name="icon"> + <iconset theme="network-workgroup"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </attribute> + <attribute name="title"> + <string>Nodes</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>9</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_12"> + <item> + <widget class="QPushButton" name="cmdNodesBack"> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="go-previous"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="nodesLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="nodeActionsButton"> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="format-justify-fill"/> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="nodeDeleteButton"> + <property name="toolTip"> + <string>Delete this node</string> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="edit-delete"/> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="nodePrefsButton"> + <property name="toolTip"> + <string>Show the preferences of this node</string> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="preferences-system"/> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="nodeStartButton"> + <property name="toolTip"> + <string>Start or stop interception of this node</string> + </property> + <property name="text"> + <string notr="true"/> + </property> + <property name="icon"> + <iconset theme="media-playback-start"/> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <property name="spacing"> + <number>0</number> + </property> + <item> + <widget class="GenericTableView" name="nodesTable"> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Plain</enum> + </property> + <property name="autoScroll"> + <bool>false</bool> + </property> + <property name="selectionBehavior"> + <enum>QAbstractItemView::SelectRows</enum> + </property> + <property name="showGrid"> + <bool>false</bool> + </property> + <property name="sortingEnabled"> + <bool>true</bool> + </property> + <attribute name="horizontalHeaderStretchLastSection"> + <bool>true</bool> + </attribute> + <attribute name="verticalHeaderVisible"> + <bool>false</bool> + </attribute> + <attribute name="verticalHeaderStretchLastSection"> + <bool>false</bool> + </attribute> + </widget> + </item> + <item> + <widget class="QScrollBar" name="verticalScrollBar"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_3"> + <attribute name="icon"> + <iconset theme="address-book-new"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </attribute> + <attribute name="title"> + <string>Rules</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="2" column="0"> + <widget class="QSplitter" name="rulesSplitter"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <widget class="QTreeWidget" name="rulesTreePanel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="animated"> + <bool>true</bool> + </property> + <property name="headerHidden"> + <bool>true</bool> + </property> + <attribute name="headerStretchLastSection"> + <bool>false</bool> + </attribute> + <column> + <property name="text"> + <string notr="true">1</string> + </property> + </column> + <column> + <property name="text"> + <string>2</string> + </property> + </column> + <item> + <property name="text"> + <string>Application rules</string> + </property> + <property name="font"> + <font> + <pointsize>10</pointsize> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="icon"> + <iconset theme="system-run"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../gustavo-iniguez-goya/opensnitch/ui/opensnitch/res</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../gustavo-iniguez-goya/opensnitch/ui/opensnitch/res</iconset> + </property> + <item> + <property name="text"> + <string>Permanent</string> + </property> + <property name="font"> + <font> + <pointsize>10</pointsize> + </font> + </property> + <property name="icon"> + <iconset theme="security-medium"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../gustavo-iniguez-goya/opensnitch/ui/opensnitch/res</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../gustavo-iniguez-goya/opensnitch/ui/opensnitch/res</iconset> + </property> + </item> + <item> + <property name="text"> + <string>Temporary</string> + </property> + <property name="font"> + <font> + <pointsize>10</pointsize> + </font> + </property> + <property name="icon"> + <iconset theme="edit-clear"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../gustavo-iniguez-goya/opensnitch/ui/opensnitch/res</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../gustavo-iniguez-goya/opensnitch/ui/opensnitch/res</iconset> + </property> + </item> + </item> + <item> + <property name="text"> + <string>Nodes</string> + </property> + <property name="font"> + <font> + <pointsize>10</pointsize> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="icon"> + <iconset theme="network-workgroup"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../gustavo-iniguez-goya/opensnitch/ui/opensnitch/res</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../gustavo-iniguez-goya/opensnitch/ui/opensnitch/res</iconset> + </property> + </item> + <item> + <property name="text"> + <string>System rules</string> + </property> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="icon"> + <iconset theme="security-high"> + <normaloff>../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + </item> + </widget> + <widget class="QWidget" name="horizontalLayoutWidget"> + <layout class="QHBoxLayout" name="horizontalLayout_19"> + <property name="spacing"> + <number>0</number> + </property> + <item> + <widget class="FirewallTableView" name="fwTable"> + <property name="autoScroll"> + <bool>false</bool> + </property> + <property name="editTriggers"> + <set>QAbstractItemView::AnyKeyPressed|QAbstractItemView::EditKeyPressed</set> + </property> + <property name="alternatingRowColors"> + <bool>true</bool> + </property> + <property name="selectionBehavior"> + <enum>QAbstractItemView::SelectRows</enum> + </property> + <property name="horizontalScrollMode"> + <enum>QAbstractItemView::ScrollPerPixel</enum> + </property> + <property name="showGrid"> + <bool>false</bool> + </property> + <property name="sortingEnabled"> + <bool>true</bool> + </property> + <attribute name="horizontalHeaderStretchLastSection"> + <bool>true</bool> + </attribute> + <attribute name="verticalHeaderMinimumSectionSize"> + <number>25</number> + </attribute> + <attribute name="verticalHeaderDefaultSectionSize"> + <number>42</number> + </attribute> + </widget> + </item> + <item> + <widget class="GenericTableView" name="rulesTable"> + <property name="selectionBehavior"> + <enum>QAbstractItemView::SelectRows</enum> + </property> + <property name="horizontalScrollMode"> + <enum>QAbstractItemView::ScrollPerPixel</enum> + </property> + <property name="showGrid"> + <bool>false</bool> + </property> + </widget> + </item> + <item> + <widget class="QScrollBar" name="rulesScrollBar"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + </widget> + </item> + </layout> + </widget> + </widget> + </item> + <item row="1" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_8"> + <item> + <widget class="QComboBox" name="comboRulesFilter"> + <item> + <property name="text"> + <string>All applications</string> + </property> + <property name="icon"> + <iconset theme="system-run"> + <normaloff>../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + </item> + <item> + <property name="text"> + <string>Permanent</string> + </property> + <property name="icon"> + <iconset theme="security-medium"> + <normaloff>../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + </item> + <item> + <property name="text"> + <string>Temporary</string> + </property> + <property name="icon"> + <iconset theme="edit-clear"> + <normaloff>../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + </item> + <item> + <property name="text"> + <string>System rules</string> + </property> + <property name="icon"> + <iconset theme="security-high"/> + </property> + </item> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_11"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>30</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </item> + <item row="0" column="0"> + <layout class="QHBoxLayout" name="rulesToolbarLayout"> + <item> + <widget class="QPushButton" name="cmdRulesBack"> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="go-previous"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="enableRuleCheck"> + <property name="text"> + <string>enable</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="nodeRuleLabel"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="ruleLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="editRuleButton"> + <property name="toolTip"> + <string>Edit rule</string> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="accessories-text-editor"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="delRuleButton"> + <property name="toolTip"> + <string>Delete rule</string> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="edit-delete"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_4"> + <attribute name="icon"> + <iconset theme="computer"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </attribute> + <attribute name="title"> + <string>Hosts</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QPushButton" name="cmdHostsBack"> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="go-previous"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="hostsLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_7"> + <property name="spacing"> + <number>0</number> + </property> + <item> + <widget class="GenericTableView" name="hostsTable"> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="selectionBehavior"> + <enum>QAbstractItemView::SelectRows</enum> + </property> + <property name="horizontalScrollMode"> + <enum>QAbstractItemView::ScrollPerPixel</enum> + </property> + <property name="showGrid"> + <bool>false</bool> + </property> + <property name="sortingEnabled"> + <bool>false</bool> + </property> + <property name="cornerButtonEnabled"> + <bool>false</bool> + </property> + <attribute name="horizontalHeaderStretchLastSection"> + <bool>true</bool> + </attribute> + <attribute name="verticalHeaderStretchLastSection"> + <bool>false</bool> + </attribute> + </widget> + </item> + <item> + <widget class="QScrollBar" name="hostsScrollBar"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_7"> + <attribute name="icon"> + <iconset theme="system-run"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </attribute> + <attribute name="title"> + <string>Applications</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QPushButton" name="cmdProcsBack"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="go-previous"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="procsLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="styleSheet"> + <string notr="true"/> + </property> + <property name="text"> + <string/> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="cmdProcDetails"> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="system-search"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_11"> + <property name="spacing"> + <number>0</number> + </property> + <item> + <widget class="GenericTableView" name="procsTable"> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Plain</enum> + </property> + <property name="selectionBehavior"> + <enum>QAbstractItemView::SelectRows</enum> + </property> + <property name="horizontalScrollMode"> + <enum>QAbstractItemView::ScrollPerPixel</enum> + </property> + <property name="showGrid"> + <bool>false</bool> + </property> + <property name="sortingEnabled"> + <bool>false</bool> + </property> + <property name="cornerButtonEnabled"> + <bool>true</bool> + </property> + <attribute name="horizontalHeaderStretchLastSection"> + <bool>true</bool> + </attribute> + <attribute name="verticalHeaderStretchLastSection"> + <bool>false</bool> + </attribute> + </widget> + </item> + <item> + <widget class="QScrollBar" name="procsScrollBar"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_2"> + <attribute name="icon"> + <iconset theme="network-server"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </attribute> + <attribute name="title"> + <string>Addresses</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_5"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_4"> + <item> + <widget class="QPushButton" name="cmdAddrsBack"> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="go-previous"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="addrsLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_13"> + <property name="spacing"> + <number>0</number> + </property> + <item> + <widget class="GenericTableView" name="addrTable"> + <property name="selectionBehavior"> + <enum>QAbstractItemView::SelectRows</enum> + </property> + <property name="horizontalScrollMode"> + <enum>QAbstractItemView::ScrollPerPixel</enum> + </property> + <property name="showGrid"> + <bool>false</bool> + </property> + <property name="sortingEnabled"> + <bool>false</bool> + </property> + <property name="cornerButtonEnabled"> + <bool>false</bool> + </property> + <attribute name="horizontalHeaderStretchLastSection"> + <bool>true</bool> + </attribute> + <attribute name="verticalHeaderStretchLastSection"> + <bool>false</bool> + </attribute> + </widget> + </item> + <item> + <widget class="QScrollBar" name="addrsScrollBar"> + <property name="maximum"> + <number>50</number> + </property> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_5"> + <attribute name="icon"> + <iconset theme="network-wired"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </attribute> + <attribute name="title"> + <string>Ports</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_6"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_5"> + <item> + <widget class="QPushButton" name="cmdPortsBack"> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="go-previous"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="portsLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_14"> + <property name="spacing"> + <number>0</number> + </property> + <item> + <widget class="GenericTableView" name="portsTable"> + <property name="selectionBehavior"> + <enum>QAbstractItemView::SelectRows</enum> + </property> + <property name="showGrid"> + <bool>false</bool> + </property> + <property name="sortingEnabled"> + <bool>false</bool> + </property> + <property name="cornerButtonEnabled"> + <bool>false</bool> + </property> + <attribute name="horizontalHeaderStretchLastSection"> + <bool>true</bool> + </attribute> + <attribute name="verticalHeaderStretchLastSection"> + <bool>false</bool> + </attribute> + </widget> + </item> + <item> + <widget class="QScrollBar" name="portsScrollBar"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_6"> + <attribute name="icon"> + <iconset theme="system-users"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </attribute> + <attribute name="title"> + <string>Users</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_7"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_6"> + <item> + <widget class="QPushButton" name="cmdUsersBack"> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="go-previous"> + <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="usersLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_15"> + <property name="spacing"> + <number>0</number> + </property> + <item> + <widget class="GenericTableView" name="usersTable"> + <property name="selectionBehavior"> + <enum>QAbstractItemView::SelectRows</enum> + </property> + <property name="showGrid"> + <bool>false</bool> + </property> + <property name="sortingEnabled"> + <bool>false</bool> + </property> + <property name="cornerButtonEnabled"> + <bool>false</bool> + </property> + <attribute name="horizontalHeaderStretchLastSection"> + <bool>true</bool> + </attribute> + <attribute name="verticalHeaderStretchLastSection"> + <bool>false</bool> + </attribute> + </widget> + </item> + <item> + <widget class="QScrollBar" name="usersScrollBar"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </widget> + </item> + <item row="4" column="0"> + <layout class="QHBoxLayout" name="horizontalLayout_9"> + <property name="spacing"> + <number>6</number> + </property> + <item> + <layout class="QHBoxLayout" name="statsLayout"> + <item> + <widget class="QLabel" name="label_5"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>10</pointsize> + <weight>75</weight> + <bold>true</bold> + <kerning>true</kerning> + </font> + </property> + <property name="text"> + <string>Connections</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="margin"> + <number>5</number> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="consLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>8</pointsize> + <kerning>true</kerning> + </font> + </property> + <property name="text"> + <string>-</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="margin"> + <number>5</number> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_7"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>10</pointsize> + <weight>75</weight> + <bold>true</bold> + <kerning>true</kerning> + </font> + </property> + <property name="text"> + <string>Dropped</string> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> + </property> + <property name="margin"> + <number>5</number> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="droppedLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>8</pointsize> + <kerning>true</kerning> + </font> + </property> + <property name="text"> + <string>-</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="margin"> + <number>5</number> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>10</pointsize> + <weight>75</weight> + <bold>true</bold> + <kerning>true</kerning> + </font> + </property> + <property name="text"> + <string>Uptime</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <property name="margin"> + <number>5</number> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="uptimeLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>8</pointsize> + <kerning>true</kerning> + </font> + </property> + <property name="text"> + <string>-</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <property name="margin"> + <number>5</number> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_3"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>10</pointsize> + <weight>75</weight> + <bold>true</bold> + <kerning>true</kerning> + </font> + </property> + <property name="text"> + <string>Rules</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <property name="margin"> + <number>5</number> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="rulesLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>8</pointsize> + <kerning>true</kerning> + </font> + </property> + <property name="text"> + <string>-</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <property name="margin"> + <number>5</number> + </property> + </widget> + </item> + </layout> + </item> + <item> + <spacer name="horizontalSpacer_10"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="label_6"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>10</pointsize> + <weight>75</weight> + <bold>true</bold> + <kerning>true</kerning> + </font> + </property> + <property name="text"> + <string>Version</string> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> + </property> + <property name="margin"> + <number>5</number> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="daemonVerLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <family>DejaVu Sans</family> + <pointsize>8</pointsize> + <kerning>true</kerning> + </font> + </property> + <property name="text"> + <string>-</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <property name="margin"> + <number>5</number> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>GenericTableView</class> + <extends>QTableView</extends> + <header>customwidgets.generictableview</header> + </customwidget> + <customwidget> + <class>FirewallTableView</class> + <extends>QTableView</extends> + <header>customwidgets.firewalltableview</header> + </customwidget> + </customwidgets> + <resources> + <include location="resources.qrc"/> + </resources> + <connections/> +</ui> diff --git a/ui/opensnitch/rules.py b/ui/opensnitch/rules.py new file mode 100644 index 0000000..485277b --- /dev/null +++ b/ui/opensnitch/rules.py @@ -0,0 +1,287 @@ +from PyQt5.QtCore import QObject, pyqtSignal + +from opensnitch.database import Database +from opensnitch.database.enums import RuleFields +from opensnitch.config import Config + +import opensnitch.proto as proto +ui_pb2, ui_pb2_grpc = proto.import_() + +import os +import json +from slugify import slugify +from datetime import datetime +from google.protobuf.json_format import MessageToJson, Parse + +DefaultRulesPath = "/etc/opensnitchd/rules" + +# date format displayed on the GUI (created column) +DBDateFieldFormat = "%Y-%m-%d %H:%M:%S" + +class Rule(): + def __init__(self): + pass + + @staticmethod + def to_bool(s): + return s == 'True' + + @staticmethod + def new_empty(): + pass + + @staticmethod + def new_from_records(records): + """Creates a new protobuf Rule from DB records. + Fields of the record are in the order defined on the DB. + """ + rule = ui_pb2.Rule(name=records.value(RuleFields.Name)) + rule.enabled = Rule.to_bool(records.value(RuleFields.Enabled)) + rule.precedence = Rule.to_bool(records.value(RuleFields.Precedence)) + rule.action = records.value(RuleFields.Action) + rule.duration = records.value(RuleFields.Duration) + rule.operator.type = records.value(RuleFields.OpType) + rule.operator.sensitive = Rule.to_bool(records.value(RuleFields.OpSensitive)) + rule.operator.operand = records.value(RuleFields.OpOperand) + rule.operator.data = "" if records.value(RuleFields.OpData) == None else str(records.value(RuleFields.OpData)) + rule.description = records.value(RuleFields.Description) + rule.nolog = Rule.to_bool(records.value(RuleFields.NoLog)) + created = int(datetime.now().timestamp()) + if records.value(RuleFields.Created) != "": + created = int(datetime.strptime( + records.value(RuleFields.Created), DBDateFieldFormat + ).timestamp()) + rule.created = created + + try: + # Operator list is always saved as json string to the db, + # so we need to load the json string. + if rule.operator.type == Config.RULE_TYPE_LIST: + operators = json.loads(rule.operator.data) + for op in operators: + rule.operator.list.extend([ + ui_pb2.Operator( + type=op['type'], + operand=op['operand'], + sensitive=False if op.get('sensitive') == None else op['sensitive'], + data="" if op.get('data') == None else op['data'] + ) + ]) + rule.operator.data = "" + except Exception as e: + print("new_from_records exception parsing operartor list:", e) + + + return rule + +class Rules(QObject): + __instance = None + updated = pyqtSignal(int) + + LOG_TAG = "[Rules]: " + + @staticmethod + def instance(): + if Rules.__instance == None: + Rules.__instance = Rules() + return Rules.__instance + + def __init__(self): + QObject.__init__(self) + self._db = Database.instance() + + def add(self, time, node, name, description, enabled, precedence, nolog, action, duration, op_type, op_sensitive, op_operand, op_data, created): + # don't add rule if the user has selected to exclude temporary + # rules + if duration in Config.RULES_DURATION_FILTER: + return + + self._db.insert("rules", + "(time, node, name, description, enabled, precedence, nolog, action, duration, operator_type, operator_sensitive, operator_operand, operator_data, created)", + (time, node, name, description, enabled, precedence, nolog, action, duration, op_type, op_sensitive, op_operand, op_data, created), + action_on_conflict="REPLACE") + + def add_rules(self, addr, rules): + try: + for _,r in enumerate(rules): + # Operator list is always saved as json string to the db. + rjson = json.loads(MessageToJson(r)) + if r.operator.type == Config.RULE_TYPE_LIST and rjson.get('operator') != None and rjson.get('operator').get('list') != None: + r.operator.data = json.dumps(rjson.get('operator').get('list')) + + self.add(datetime.now().strftime(DBDateFieldFormat), + addr, + r.name, r.description, str(r.enabled), + str(r.precedence), str(r.nolog), r.action, r.duration, + r.operator.type, + str(r.operator.sensitive), + r.operator.operand, r.operator.data, + str(datetime.fromtimestamp(r.created).strftime(DBDateFieldFormat))) + + return True + except Exception as e: + print(self.LOG_TAG + " exception adding node rules to db: ", e) + return False + + def delete(self, name, addr, callback): + rule = ui_pb2.Rule(name=name) + rule.enabled = False + rule.action = "" + rule.duration = "" + rule.operator.type = "" + rule.operator.operand = "" + rule.operator.data = "" + + if not self._db.delete_rule(rule.name, addr): + return None + + return rule + + def delete_by_field(self, field, values): + return self._db.delete_rules_by_field(field, values) + + def exists(self, rule, node_addr): + return self._db.rule_exists(rule, node_addr) + + def new_unique_name(self, rule_name, node_addr, prefix): + """generate a new name, if the supplied one already exists + """ + if self._db.get_rule(rule_name, node_addr).next() == False: + return rule_name + + for idx in range(0, 100): + new_rule_name = "{0}-{1}".format(rule_name, idx) + if self._db.get_rule(new_rule_name, node_addr).next() == False: + return new_rule_name + + return rule_name + + def update_time(self, time, name, addr): + """Updates the time of a rule, whenever a new connection matched a + rule. + """ + self._db.update("rules", + "time=?", + (time, name, addr), + "name=? AND node=?", + action_on_conflict="OR REPLACE" + ) + + def _timestamp_to_rfc3339(self, time): + """converts timestamp to rfc3339 format""" + return "{0}Z".format( + datetime.fromtimestamp(time).isoformat(timespec='microseconds') + ) + + def rule_to_json(self, node, rule_name): + try: + records = self._db.get_rule(rule_name, node) + if records == None or records == -1: + return None + if not records.next(): + return None + rule = Rule.new_from_records(records) + # exclude this field when exporting to json + tempRule = MessageToJson(rule) + jRule = json.loads(tempRule) + jRule['created'] = self._timestamp_to_rfc3339(rule.created) + return json.dumps(jRule, indent=" ") + except Exception as e: + print("rule_to_json() exception:", e) + return None + + def _export_rule_common(self, node, records, outdir): + try: + rule = Rule.new_from_records(records) + rulename = rule.name + if ".json" not in rulename: + rulename = rulename + ".json" + with open(outdir + "/" + rulename, 'w') as jsfile: + actual_json_text = MessageToJson(rule) + jRule = json.loads(actual_json_text) + jRule['created'] = self._timestamp_to_rfc3339(rule.created) + actual_json_text = json.dumps(jRule, indent=" ") + jsfile.write( actual_json_text ) + + return True + except Exception as e: + print(self.LOG_TAG, "export_rules(", node, outdir, ") exception:", e) + + return False + + def export_rule(self, node, rule_name, outdir): + """Gets the rule from the DB and writes it out to a directory. + A new directory per node will be created. + """ + try: + records = self._db.get_rule(rule_name, node) + if records.next() == False: + print("export_rule() get_error 2:", records) + return False + + rulesdir = outdir + "/" + slugify(node) + try: + os.makedirs(rulesdir, 0o700) + except Exception as e: + print("exception creating dirs:", e) + + return self._export_rule_common(node, records, rulesdir) + + except Exception as e: + print(self.LOG_TAG, "export_rules(", node, rulesdir, ") exception:", e) + + return False + + def export_rules(self, node, outdir): + """Gets the rules from the DB and writes them out to a directory. + A new directory per node will be created. + """ + records = self._db.get_rules(node) + if records == None: + return False + + rulesdir = outdir + "/" + slugify(node) + try: + os.makedirs(rulesdir, 0o700) + except Exception as e: + print("exception creating dirs:", e) + try: + while records.next() != False: + self._export_rule_common(node, records, rulesdir) + + except Exception as e: + print(self.LOG_TAG, "export_rules(", node, rulesdir, ") exception:", e) + return False + + return True + + def import_rules(self, rulesdir): + """Read a directory with rules in json format, and parse them out to protobuf + Returns a list of rules on success, or None on error. + """ + try: + rules = [] + for rulename in os.listdir(rulesdir): + with open(rulesdir + "/" + rulename, 'r') as f: + jsrule = f.read() + # up until v1.6.5/v1.7.0, 'created' field was exported as timestamp. + # since > v1.6.5 it's exported in rfc3339 format, so if we fail to + # parse the rule, we'll try to convert the 'created' value from + # timestamp to rfc3339. + try: + pb_rule = Parse(text=jsrule, message=ui_pb2.Rule(), ignore_unknown_fields=True) + except: + jRule = json.loads(jsrule) + created = int(datetime.strptime( + jRule['created'], "%Y-%m-%dT%H:%M:%S.%fZ" + ).timestamp()) + jRule['created'] = created + jsrule = json.dumps(jRule) + pb_rule = Parse(text=jsrule, message=ui_pb2.Rule(), ignore_unknown_fields=True) + rules.append(pb_rule) + + return rules + except Exception as e: + print(self.LOG_TAG, "import_rules() exception:", e) + + return None diff --git a/ui/opensnitch/service.py b/ui/opensnitch/service.py new file mode 100644 index 0000000..e983556 --- /dev/null +++ b/ui/opensnitch/service.py @@ -0,0 +1,907 @@ +from PyQt5 import QtWidgets, QtGui, QtCore +from PyQt5.QtCore import QCoreApplication as QC + +from datetime import datetime, timedelta +from threading import Thread, Lock, Event +import grpc +import os +import sys +import json +import copy + +path = os.path.abspath(os.path.dirname(__file__)) +sys.path.append(path) + +import opensnitch.proto as proto +ui_pb2, ui_pb2_grpc = proto.import_() + +from opensnitch.dialogs.prompt import PromptDialog +from opensnitch.dialogs.stats import StatsDialog + +from opensnitch.notifications import DesktopNotifications +from opensnitch.firewall import Rules as FwRules +from opensnitch.nodes import Nodes +from opensnitch.config import Config +from opensnitch.version import version +from opensnitch.database import Database +from opensnitch.utils import Utils, CleanerTask, Themes +from opensnitch.utils import Message, languages +from opensnitch.utils.xdg import Autostart + +class UIService(ui_pb2_grpc.UIServicer, QtWidgets.QGraphicsObject): + _new_remote_trigger = QtCore.pyqtSignal(str, ui_pb2.PingRequest) + _node_actions_trigger = QtCore.pyqtSignal(dict) + _update_stats_trigger = QtCore.pyqtSignal(str, str, ui_pb2.PingRequest) + _add_alert_trigger = QtCore.pyqtSignal(str, str, ui_pb2.Alert) + _version_warning_trigger = QtCore.pyqtSignal(str, str) + _status_change_trigger = QtCore.pyqtSignal(bool) + _notification_callback = QtCore.pyqtSignal(ui_pb2.NotificationReply) + _show_message_trigger = QtCore.pyqtSignal(str, str, int, int) + + # .desktop filename located under /usr/share/applications/ + DESKTOP_FILENAME = "opensnitch_ui.desktop" + + def __init__(self, app, on_exit, start_in_bg=False): + super(UIService, self).__init__() + + + self.MENU_ENTRY_STATS = QtCore.QCoreApplication.translate("contextual_menu", "Open main window") + self.MENU_ENTRY_FW_ENABLE = QtCore.QCoreApplication.translate("contextual_menu", "Enable") + self.MENU_ENTRY_FW_DISABLE = QtCore.QCoreApplication.translate("contextual_menu", "Disable") + self.MENU_ENTRY_HELP = QtCore.QCoreApplication.translate("contextual_menu", "Help") + self.MENU_ENTRY_CLOSE = QtCore.QCoreApplication.translate("contextual_menu", "Close") + + # set of actions that must be performed on the main thread + self.NODE_ADD = 0 + self.NODE_UPDATE = 1 + self.NODE_DELETE = 2 + self.ADD_RULE = 3 + self.DELETE_RULE = 4 + + self._cfg = Config.init() + self._db = Database.instance() + db_file=self._cfg.getSettings(self._cfg.DEFAULT_DB_FILE_KEY) + db_jrnl_wal=self._cfg.getBool(Config.DEFAULT_DB_JRNL_WAL) + db_status, db_error = self._db.initialize( + dbtype=self._cfg.getInt(self._cfg.DEFAULT_DB_TYPE_KEY), + dbfile=db_file, + dbjrnl_wal=db_jrnl_wal + ) + if db_status is False: + Message.ok( + QtCore.QCoreApplication.translate("preferences", "Warning"), + QtCore.QCoreApplication.translate("preferences", + "The DB is corrupted and it's not safe to continue.<br>\ + Remove, backup or recover the file before continuing.<br><br>\ + Corrupted database file: {0}".format(db_file)), + QtWidgets.QMessageBox.Warning) + sys.exit(-1) + + self._db_sqlite = self._db.get_db() + self._last_ping = None + self._version_warning_shown = False + self._asking = False + self._connected = False + self._fw_enabled = False + self._path = os.path.abspath(os.path.dirname(__file__)) + self._app = app + self._on_exit = on_exit + self._exit = False + self._msg = QtWidgets.QMessageBox() + self._remote_lock = Lock() + self._remote_stats = {} + self._autostart = Autostart() + + self.translator = None + self._init_translation() + self._themes = Themes() + self._desktop_notifications = DesktopNotifications() + self._setup_interfaces() + self._setup_icons() + self._prompt_dialog = PromptDialog(appicon=self.white_icon) + self._stats_dialog = StatsDialog(dbname="general", db=self._db, appicon=self.white_icon) + self._setup_tray() + self._setup_slots() + + self._nodes = Nodes.instance() + self._nodes.reset_status() + + self._last_stats = {} + self._last_items = { + 'hosts':{}, + 'procs':{}, + 'addrs':{}, + 'ports':{}, + 'users':{} + } + + if not start_in_bg: + self._show_gui_if_tray_not_available() + + self._cleaner = None + if self._cfg.getBool(Config.DEFAULT_DB_PURGE_OLDEST): + self._start_db_cleaner() + self._cfg.setRulesDurationFilter( + self._cfg.getBool(self._cfg.DEFAULT_IGNORE_RULES), + self._cfg.getInt(self._cfg.DEFAULT_IGNORE_TEMPORARY_RULES) + ) + if self._cfg.getBool(self._cfg.DEFAULT_IGNORE_RULES): + self._nodes.delete_rule_by_field(Config.DURATION_FIELD, Config.RULES_DURATION_FILTER) + + # https://gist.github.com/pklaus/289646 + def _setup_interfaces(self): + namestr, outbytes = Utils.get_interfaces() + self._interfaces = {} + for i in range(0, outbytes, 40): + name = namestr[i:i+16].split(b'\0', 1)[0] + addr = namestr[i+20:i+24] + self._interfaces[name] = "%d.%d.%d.%d" % (int(addr[0]), int(addr[1]), int(addr[2]), int(addr[3])) + + def _setup_slots(self): + # https://stackoverflow.com/questions/40288921/pyqt-after-messagebox-application-quits-why + self._app.setQuitOnLastWindowClosed(False) + self._version_warning_trigger.connect(self._on_diff_versions) + self._new_remote_trigger.connect(self._on_new_remote) + self._node_actions_trigger.connect(self._on_node_actions) + self._update_stats_trigger.connect(self._on_update_stats) + self._add_alert_trigger.connect(self._on_new_alert) + self._status_change_trigger.connect(self._on_status_changed) + self._stats_dialog._shown_trigger.connect(self._on_stats_dialog_shown) + self._stats_dialog._status_changed_trigger.connect(self._on_stats_status_changed) + self._stats_dialog.settings_saved.connect(self._on_settings_saved) + self._stats_dialog.close_trigger.connect(self._on_close) + self._show_message_trigger.connect(self._show_systray_message) + + def _setup_icons(self): + self.off_image = QtGui.QPixmap(os.path.join(self._path, "res/icon-off.png")) + self.off_icon = QtGui.QIcon() + self.off_icon.addPixmap(self.off_image, QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.white_image = QtGui.QPixmap(os.path.join(self._path, "res/icon-white.svg")) + self.white_icon = QtGui.QIcon() + self.white_icon.addPixmap(self.white_image, QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.red_image = QtGui.QPixmap(os.path.join(self._path, "res/icon-red.png")) + self.red_icon = QtGui.QIcon() + self.red_icon.addPixmap(self.red_image, QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.pause_image = QtGui.QPixmap(os.path.join(self._path, "res/icon-pause.png")) + self.pause_icon = QtGui.QIcon() + self.pause_icon.addPixmap(self.pause_image, QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.alert_image = QtGui.QPixmap(os.path.join(self._path, "res/icon-alert.png")) + self.alert_icon = QtGui.QIcon() + self.alert_icon.addPixmap(self.alert_image, QtGui.QIcon.Normal, QtGui.QIcon.Off) + + self._app.setWindowIcon(self.white_icon) + # NOTE: only available since pyqt 5.7 + if hasattr(self._app, "setDesktopFileName"): + self._app.setDesktopFileName(self.DESKTOP_FILENAME) + + def _setup_tray(self): + self._tray = QtWidgets.QSystemTrayIcon(self.off_icon) + self._tray.show() + + self._menu = QtWidgets.QMenu() + self._tray.setContextMenu(self._menu) + self._tray.activated.connect(self._on_tray_icon_activated) + + self._menu.addAction(self.MENU_ENTRY_STATS).triggered.connect(self._show_stats_dialog) + self._menu_enable_fw = self._menu.addAction(self.MENU_ENTRY_FW_DISABLE) + self._menu_enable_fw.setEnabled(False) + self._menu_enable_fw.triggered.connect(self._on_enable_interception_clicked) + + self._menu.addSeparator() + self._menu_autostart = self._menu.addAction("Autostart") + self._menu_autostart.setCheckable(True) + self._menu_autostart.setChecked(self._autostart.isEnabled()) + self._menu_autostart.triggered.connect(self._on_switch_autostart) + self._menu.addSeparator() + + self._menu.addAction(self.MENU_ENTRY_HELP).triggered.connect( + lambda: QtGui.QDesktopServices.openUrl(QtCore.QUrl(Config.HELP_CONFIG_URL)) + ) + self._menu.addAction(self.MENU_ENTRY_CLOSE).triggered.connect(self._on_close) + + self._menu.aboutToShow.connect(self._on_show_menu) + + def _on_switch_autostart(self): + try: + self._autostart.enable(self._menu_autostart.isChecked()) + except Exception as e: + self._desktop_notifications.show( + QC.translate("stats", "Warning"), + QC.translate("stats", str(e)) + ) + + def _on_show_menu(self): + self._menu_autostart.setChecked(self._autostart.isEnabled()) + + def _show_gui_if_tray_not_available(self): + """If the system tray is not available or ready, show the GUI after + 10s. This delay helps to skip showing up the GUI when DEs' autologin is on. + """ + tray = self._tray + gui = self._stats_dialog + def __show_gui(): + if not tray.isSystemTrayAvailable(): + self._show_systray_msg_error() + gui.show() + + QtCore.QTimer.singleShot(10000, __show_gui) + + def _show_systray_msg_error(self): + print("") + print("WARNING: system tray not available. On GNOME you need the extension gnome-shell-extension-appindicator.") + print("\tRead more:", Config.HELP_SYSTRAY_WARN) + print("\tIf you want to start OpenSnitch GUI in background even if tray not available, use --background argument.") + print("") + + hide_msg = self._cfg.getBool(Config.DEFAULT_HIDE_SYSTRAY_WARN) + if hide_msg: + return + self._desktop_notifications.show( + QC.translate("stats", "WARNING"), + QC.translate("stats", """System tray not available. Read more: +{0} +""".format(Config.HELP_SYSTRAY_WARN)), + os.path.join(self._path, "res/icon-white.svg") + ) + self._cfg.setSettings(Config.DEFAULT_HIDE_SYSTRAY_WARN, True) + + def _on_tray_icon_activated(self, reason): + if reason == QtWidgets.QSystemTrayIcon.Trigger or reason == QtWidgets.QSystemTrayIcon.MiddleClick: + if self._stats_dialog.isVisible() and not self._stats_dialog.isMinimized(): + self._stats_dialog.hide() + elif self._stats_dialog.isVisible() and self._stats_dialog.isMinimized() and not self._stats_dialog.isMaximized(): + self._stats_dialog.hide() + self._stats_dialog.showNormal() + elif self._stats_dialog.isVisible() and self._stats_dialog.isMinimized() and self._stats_dialog.isMaximized(): + self._stats_dialog.hide() + self._stats_dialog.showMaximized() + else: + self._stats_dialog.show() + + def _on_close(self): + self._exit = True + self._tray.setIcon(self.off_icon) + self._app.processEvents() + self._nodes.stop_notifications() + self._nodes.update_all(Nodes.OFFLINE) + self._db.vacuum() + self._db.optimize() + self._db.close() + self._stop_db_cleaner() + self._on_exit() + + def _show_stats_dialog(self): + if self._connected and self._fw_enabled: + self._tray.setIcon(self.white_icon) + self._stats_dialog.show() + + @QtCore.pyqtSlot(bool) + def _on_stats_status_changed(self, enabled): + self._update_fw_status(enabled) + + @QtCore.pyqtSlot(bool) + def _on_status_changed(self, enabled): + self._set_daemon_connected(enabled) + + @QtCore.pyqtSlot(str, str) + def _on_diff_versions(self, daemon_ver, ui_ver): + if self._version_warning_shown == False: + self._msg.setIcon(QtWidgets.QMessageBox.Warning) + self._msg.setWindowTitle("OpenSnitch version mismatch!") + self._msg.setText(("You are running version <b>%s</b> of the daemon, while the UI is at version " + \ + "<b>%s</b>, they might not be fully compatible.") % (daemon_ver, ui_ver)) + self._msg.setStandardButtons(QtWidgets.QMessageBox.Ok) + self._msg.show() + self._version_warning_shown = True + + @QtCore.pyqtSlot(str, str, ui_pb2.PingRequest) + def _on_update_stats(self, proto, addr, request): + main_need_refresh, details_need_refresh = self._populate_stats(self._db, proto, addr, request.stats) + is_local_request = self._is_local_request(proto, addr) + self._stats_dialog.update(is_local_request, request.stats, main_need_refresh or details_need_refresh) + + @QtCore.pyqtSlot(str, str, ui_pb2.Alert) + def _on_new_alert(self, proto, addr, alert): + try: + is_local = self._is_local_request(proto, addr) + + icon = QtWidgets.QSystemTrayIcon.Information + _title = QtCore.QCoreApplication.translate("messages", "Info") + atype = "INFO" + if alert.type == ui_pb2.Alert.ERROR: + atype = "ERROR" + _title = QtCore.QCoreApplication.translate("messages", "Error") + icon = QtWidgets.QSystemTrayIcon.Critical + if alert.type == ui_pb2.Alert.WARNING: + atype = "WARNING" + _title = QtCore.QCoreApplication.translate("messages", "Warning") + icon = QtWidgets.QSystemTrayIcon.Warning + + body = "" + what = "GENERIC" + if alert.what == ui_pb2.Alert.GENERIC: + body = alert.text + elif alert.what == ui_pb2.Alert.KERNEL_EVENT: + body = "%s\n%s" % (alert.text, alert.proc.path) + what = "KERNEL EVENT" + if is_local is False: + body = "node: {0}:{1}\n\n{2}\n{3}".format(proto, addr, alert.text, alert.proc.path) + + if alert.action == ui_pb2.Alert.SHOW_ALERT: + + urgency = DesktopNotifications.URGENCY_NORMAL + if alert.priority == ui_pb2.Alert.LOW: + urgency = DesktopNotifications.URGENCY_LOW + elif alert.priority == ui_pb2.Alert.HIGH: + urgency = DesktopNotifications.URGENCY_CRITICAL + + self._show_message_trigger.emit(_title, body, icon, urgency) + + else: + print("PostAlert() unknown alert action:", alert.action) + + + except Exception as e: + print("PostAlert() exception:", e) + return ui_pb2.MsgResponse(id=1) + + @QtCore.pyqtSlot(str, ui_pb2.PingRequest) + def _on_new_remote(self, addr, request): + self._remote_stats[addr] = { + 'last_ping': datetime.now(), + 'dialog': StatsDialog(address=addr, dbname=addr, db=self._db) + } + self._remote_stats[addr]['dialog'].daemon_connected = True + self._remote_stats[addr]['dialog'].update(addr, request.stats) + self._remote_stats[addr]['dialog'].show() + + @QtCore.pyqtSlot() + def _on_stats_dialog_shown(self): + if self._connected: + if self._fw_enabled: + self._tray.setIcon(self.white_icon) + else: + self._tray.setIcon(self.pause_icon) + else: + self._tray.setIcon(self.off_icon) + + @QtCore.pyqtSlot(ui_pb2.NotificationReply) + def _on_notification_reply(self, reply): + if reply.code == ui_pb2.ERROR: + self._tray.showMessage("Error", + reply.data, + QtWidgets.QSystemTrayIcon.Information, + 5000) + + def _on_remote_stats_menu(self, address): + self._remote_stats[address]['dialog'].show() + + @QtCore.pyqtSlot(str, str, int, int) + def _show_systray_message(self, title, body, icon, urgency): + def callback_open_clicked(notifObject, action): + if action == DesktopNotifications.ACTION_ID_OPEN: + self._stats_dialog.show() + #self._stats_dialog.raise() + self._stats_dialog.activateWindow() + + if self._desktop_notifications.are_enabled(): + timeout = self._cfg.getInt(Config.DEFAULT_TIMEOUT_KEY, 15) + + if self._desktop_notifications.is_available() and self._cfg.getInt(Config.NOTIFICATIONS_TYPE, 1) == Config.NOTIFICATION_TYPE_SYSTEM: + try: + self._desktop_notifications.show( + title, + body, + os.path.join(self._path, "res/icon-white.svg"), + callback=callback_open_clicked + ) + except: + self._tray.showMessage(title, body, icon, timeout * 1000) + else: + self._tray.showMessage(title, body, icon, timeout * 1000) + + if icon == QtWidgets.QSystemTrayIcon.NoIcon: + self._tray.setIcon(self.alert_icon) + + def _on_enable_interception_clicked(self): + self._enable_interception(self._fw_enabled) + + @QtCore.pyqtSlot() + def _on_settings_saved(self): + if self._cfg.getBool(Config.DEFAULT_DB_PURGE_OLDEST): + if self._cleaner != None: + self._stop_db_cleaner() + self._start_db_cleaner() + elif self._cfg.getBool(Config.DEFAULT_DB_PURGE_OLDEST) == False and self._cleaner != None: + self._stop_db_cleaner() + + theme_idx, theme_name, theme_density = self._themes.get_saved_theme() + if theme_idx > 0: + self._themes.load_theme(self._app) + + def _init_translation(self): + if self.translator: + self._app.removeTranslator(self.translator) + saved_lang = self._cfg.getSettings(Config.DEFAULT_LANGUAGE) + self.translator = languages.init(saved_lang) + self._app.installTranslator(self.translator) + + def _stop_db_cleaner(self): + if self._cleaner != None: + self._cleaner.stop() + self._cleaner = None + + def _start_db_cleaner(self): + def _cleaner_task(db): + oldest = self._cfg.getInt(self._cfg.DEFAULT_DB_MAX_DAYS, 1) + db.purge_oldest(oldest) + + interval = self._cfg.getInt(self._cfg.DEFAULT_DB_PURGE_INTERVAL, 5) + self._cleaner = CleanerTask(interval, _cleaner_task) + self._cleaner.start() + + def _update_fw_status(self, enabled): + """_update_fw_status updates the status of the menu entry + to disable or enable the firewall of the daemon. + """ + self._fw_enabled = enabled + if self._connected == False: + return + + self._stats_dialog.update_interception_status(enabled) + if enabled: + self._tray.setIcon(self.white_icon) + self._menu_enable_fw.setText(self.MENU_ENTRY_FW_DISABLE) + else: + self._tray.setIcon(self.pause_icon) + self._menu_enable_fw.setText(self.MENU_ENTRY_FW_ENABLE) + + def _set_daemon_connected(self, connected): + """_set_daemon_connected only updates the connection status of the daemon(s), + regardless if the fw is enabled or not. + There're 3 states: + - daemon connected + - daemon not connected + - daemon connected and firewall enabled/disabled + """ + self._stats_dialog.daemon_connected = connected + self._connected = connected + + # if there're more than 1 node, override connection status + if self._nodes.count() >= 1: + self._connected = True + self._stats_dialog.daemon_connected = True + + if self._nodes.count() == 1: + self._menu_enable_fw.setEnabled(True) + + if self._nodes.count() == 0 or self._nodes.count() > 1: + self._menu_enable_fw.setEnabled(False) + + self._stats_dialog.update_status() + + if self._connected: + self._tray.setIcon(self.white_icon) + else: + self._fw_enabled = False + self._tray.setIcon(self.off_icon) + + def _enable_interception(self, enable): + if self._connected == False: + return + if self._nodes.count() == 0: + self._tray.showMessage("No nodes connected", + "", + QtWidgets.QSystemTrayIcon.Information, + 5000) + return + if self._nodes.count() > 1: + print("enable interception for all nodes not supported yet") + return + + if enable: + nid, noti = self._nodes.stop_interception(_callback=self._notification_callback) + else: + nid, noti = self._nodes.start_interception(_callback=self._notification_callback) + + self._fw_enabled = not enable + + self._stats_dialog._status_changed_trigger.emit(not enable) + + def _is_local_request(self, proto, addr): + if proto == "unix" or proto == "unix-abstract": + return True + + elif proto == "ipv4" or proto == "ipv6": + for name, ip in self._interfaces.items(): + if addr == ip: + return True + + return False + + def _get_peer(self, peer): + """ + server -> client + 127.0.0.1:50051 -> ipv4:127.0.0.1:52032 + [::]:50051 -> ipv6:[::1]:59680 + 0.0.0.0:50051 -> ipv6:[::1]:59654 + """ + return self._nodes.get_addr(peer) + + def _delete_node(self, peer): + try: + proto, addr = self._get_peer(peer) + if addr in self._last_stats: + del self._last_stats[addr] + for table in self._last_items: + if addr in self._last_items[table]: + del self._last_items[table][addr] + + self._nodes.update(peer, Nodes.OFFLINE) + self._nodes.delete(peer) + self._stats_dialog.update(True, None, True) + except Exception as e: + print("_delete_node() exception:", e) + + def _populate_stats(self, db, proto, addr, stats): + main_need_refresh = False + details_need_refresh = False + try: + if db == None: + print("populate_stats() db None") + return main_need_refresh, details_need_refresh + + peer = proto+":"+addr + _node = self._nodes.get_node(peer) + if _node == None: + return main_need_refresh, details_need_refresh + + # TODO: move to nodes.add_node() + version = _node['data'].version if _node != None else "" + hostname = _node['data'].name if _node != None else "" + db.insert("nodes", + "(addr, status, hostname, daemon_version, daemon_uptime, " \ + "daemon_rules, cons, cons_dropped, version, last_connection)", + (peer, Nodes.ONLINE, hostname, stats.daemon_version, str(timedelta(seconds=stats.uptime)), + stats.rules, stats.connections, stats.dropped, + version, datetime.now().strftime("%Y-%m-%d %H:%M:%S"))) + + if addr not in self._last_stats: + self._last_stats[addr] = [] + + db.transaction() + for event in stats.events: + if event.unixnano in self._last_stats[addr]: + continue + main_need_refresh=True + db.insert("connections", + "(time, node, action, protocol, src_ip, src_port, dst_ip, dst_host, dst_port, uid, pid, process, process_args, process_cwd, rule)", + (str(datetime.fromtimestamp(event.unixnano/1000000000)), peer, event.rule.action, + event.connection.protocol, event.connection.src_ip, str(event.connection.src_port), + event.connection.dst_ip, event.connection.dst_host, str(event.connection.dst_port), + str(event.connection.user_id), str(event.connection.process_id), + event.connection.process_path, " ".join(event.connection.process_args), + event.connection.process_cwd, event.rule.name), + action_on_conflict="IGNORE" + ) + self._nodes.update_rule_time( + str(datetime.fromtimestamp(event.unixnano/1000000000)), + event.rule.name, + peer + ) + db.commit() + + details_need_refresh = self._populate_stats_details(db, addr, stats) + self._last_stats[addr] = [] + for event in stats.events: + self._last_stats[addr].append(event.unixnano) + except Exception as e: + print("_populate_stats() exception: ", e) + + return main_need_refresh, details_need_refresh + + def _populate_stats_details(self, db, addr, stats): + need_refresh = False + changed = self._populate_stats_events(db, addr, stats, "hosts", ("what", "hits"), (1,2), stats.by_host.items()) + if changed: need_refresh = True + changed = self._populate_stats_events(db, addr, stats, "procs", ("what", "hits"), (1,2), stats.by_executable.items()) + if changed: need_refresh = True + changed = self._populate_stats_events(db, addr, stats, "addrs", ("what", "hits"), (1,2), stats.by_address.items()) + if changed: need_refresh = True + changed = self._populate_stats_events(db, addr, stats, "ports", ("what", "hits"), (1,2), stats.by_port.items()) + if changed: need_refresh = True + changed = self._populate_stats_events(db, addr, stats, "users", ("what", "hits"), (1,2), stats.by_uid.items()) + if changed: need_refresh = True + + return need_refresh + + def _populate_stats_events(self, db, addr, stats, table, colnames, cols, items): + fields = [] + values = [] + need_refresh = False + try: + if addr not in self._last_items[table].keys(): + self._last_items[table][addr] = {} + if items == self._last_items[table][addr]: + return need_refresh + + for row, event in enumerate(items): + if event in self._last_items[table][addr]: + continue + need_refresh = True + what, hits = event + # FIXME: this is suboptimal + # BUG: there can be users with same id on different machines but with different names + if table == "users": + what = Utils.get_user_id(what) + fields.append(what) + values.append(int(hits)) + # FIXME: default action on conflict is to replace. If there're multiple nodes connected, + # stats are painted once per node on each update. + if need_refresh: + db.insert_batch(table, colnames, cols, fields, values) + + self._last_items[table][addr] = items + except Exception as e: + print("details exception: ", e) + + return need_refresh + + def _overwrite_nodes_config(self, node_config): + """Overwrite daemon's DefaultAction value, with the one defined by the GUI. + It'll only be valid while the daemon is connected to the GUI (it's not saved to disk). + """ + newconf = copy.deepcopy(node_config) + _default_action = self._cfg.getInt(self._cfg.DEFAULT_ACTION_KEY) + try: + temp_cfg = json.loads(newconf.config) + if _default_action == Config.ACTION_ALLOW_IDX: + temp_cfg['DefaultAction'] = Config.ACTION_ALLOW + else: + temp_cfg['DefaultAction'] = Config.ACTION_DENY + + print("Setting daemon DefaultAction to:", temp_cfg['DefaultAction']) + + newconf.config = json.dumps(temp_cfg) + except Exception as e: + print("error parsing node's configuration:", e) + return node_config + + return newconf + + @QtCore.pyqtSlot(dict) + def _on_node_actions(self, kwargs): + if kwargs['action'] == self.NODE_ADD: + n, addr = self._nodes.add(kwargs['peer'], kwargs['node_config']) + if n != None: + self._nodes.add_fw_rules( + addr, + FwRules.to_dict(kwargs['node_config'].systemFirewall.SystemRules) + ) + self._status_change_trigger.emit(True) + # if there're more than one node, we can't update the status + # based on the fw status, only if the daemon is running or not + if self._nodes.count() <= 1: + self._update_fw_status(kwargs['node_config'].isFirewallRunning) + else: + self._update_fw_status(True) + elif kwargs['action'] == self.ADD_RULE: + rule = kwargs['rule'] + proto, addr = self._get_peer(kwargs['peer']) + self._nodes.add_rule((datetime.now().strftime("%Y-%m-%d %H:%M:%S")), + "{0}:{1}".format(proto, addr), + rule.name, rule.description, str(rule.enabled), + str(rule.precedence), str(rule.nolog), rule.action, rule.duration, + rule.operator.type, str(rule.operator.sensitive), rule.operator.operand, + rule.operator.data, + str(datetime.fromtimestamp(rule.created).strftime("%Y-%m-%d %H:%M:%S")) + ) + if rule.operator.type == Config.RULE_TYPE_LIST: + # reset list operator data before sending it back to the + # daemon. + rule.operator.data = "" + + elif kwargs['action'] == self.DELETE_RULE: + self._db.delete_rule(kwargs['name'], kwargs['addr']) + + elif kwargs['action'] == self.NODE_DELETE: + self._delete_node(kwargs['peer']) + + def OpenWindow(self): + self._stats_dialog.show() + + def close(self): + self._on_close() + + def PostAlert(self, alert, context): + proto, addr = self._get_peer(context.peer()) + self._add_alert_trigger.emit(proto, addr, alert) + return ui_pb2.MsgResponse(id=0) + + def Ping(self, request, context): + try: + self._last_ping = datetime.now() + if Utils.check_versions(request.stats.daemon_version): + self._version_warning_trigger.emit(request.stats.daemon_version, version) + + proto, addr = self._get_peer(context.peer()) + # do not update db here, do it on the main thread + self._update_stats_trigger.emit(proto, addr, request) + #else: + # with self._remote_lock: + # # XXX: disable this option for now + # # opening several dialogs only updates one of them. + # if addr not in self._remote_stats: + # self._new_remote_trigger.emit(addr, request) + # else: + # self._populate_stats(self._remote_stats[addr]['dialog'].get_db(), proto, addr, request.stats) + # self._remote_stats[addr]['dialog'].update(addr, request.stats) + + except Exception as e: + print("Ping exception: ", e) + + return ui_pb2.PingReply(id=request.id) + + def AskRule(self, request, context): + #def callback(ntf, action, connection): + # TODO + + #if self._desktop_notifications.support_actions(): + # self._desktop_notifications.ask(request, callback) + + # TODO: allow connections originated from ourselves: os.getpid() == request.pid) + self._asking = True + peer = context.peer() + proto, addr = self._get_peer(peer) + rule, timeout_triggered = self._prompt_dialog.promptUser(request, self._is_local_request(proto, addr), peer) + self._last_ping = datetime.now() + self._asking = False + if rule == None: + return None + + if timeout_triggered: + _title = request.process_path + if _title == "": + _title = "%s:%d (%s)" % (request.dst_host if request.dst_host != "" else request.dst_ip, request.dst_port, request.protocol) + + + node_text = "" if self._is_local_request(proto, addr) else "on node {0}:{1}".format(proto, addr) + self._show_message_trigger.emit(_title, + "{0} action applied {1}\nCommand line: {2}" + .format(rule.action, node_text, " ".join(request.process_args)), + QtWidgets.QSystemTrayIcon.NoIcon, + DesktopNotifications.URGENCY_NORMAL) + + + if rule.duration in Config.RULES_DURATION_FILTER: + self._node_actions_trigger.emit( + { + 'action': self.DELETE_RULE, + 'name': rule.name, + 'addr': peer + } + ) + else: + self._node_actions_trigger.emit( + { + 'action': self.ADD_RULE, + 'peer': peer, + 'rule': rule + } + ) + + return rule + + def Subscribe(self, node_config, context): + """ + Accept and collect nodes. It keeps a connection open with each + client, in order to send them notifications. + + @doc: https://grpc.github.io/grpc/python/grpc.html#service-side-context + """ + # if the exit mark is set, don't accept new connections. + # db vacuum operation may take a lot of time to complete. + if self._exit: + context.cancel() + return + try: + self._node_actions_trigger.emit({ + 'action': self.NODE_ADD, + 'peer': context.peer(), + 'node_config': node_config + }) + # force events processing, to add the node ^ before the + # Notifications() call arrives. + self._app.processEvents() + + proto, addr = self._get_peer(context.peer()) + if self._is_local_request(proto, addr) == False: + self._show_message_trigger.emit( + QtCore.QCoreApplication.translate("stats", "New node connected"), + "({0})".format(context.peer()), + QtWidgets.QSystemTrayIcon.Information, + DesktopNotifications.URGENCY_LOW + ) + except Exception as e: + print("[Notifications] exception adding new node:", e) + context.cancel() + + newconf = self._overwrite_nodes_config(node_config) + + return newconf + + def Notifications(self, node_iter, context): + """ + Accept and collect nodes. It keeps a connection open with each + client, in order to send them notifications. + + @doc: https://grpc.github.io/grpc/python/grpc.html#service-side-context + @doc: https://grpc.io/docs/what-is-grpc/core-concepts/ + """ + proto, addr = self._get_peer(context.peer()) + _node = self._nodes.get_node("%s:%s" % (proto, addr)) + if _node == None: + return + + stop_event = Event() + def _on_client_closed(): + stop_event.set() + self._node_actions_trigger.emit( + {'action': self.NODE_DELETE, + 'peer': context.peer(), + }) + + self._status_change_trigger.emit(False) + # TODO: handle the situation when a node disconnects, and the + # remaining node has the fw disabled. + #if self._nodes.count() == 1: + # nd = self._nodes.get_nodes() + # if nd[0].get_config().isFirewallRunning: + + if self._is_local_request(proto, addr) == False: + self._show_message_trigger.emit("node exited", + "({0})".format(context.peer()), + QtWidgets.QSystemTrayIcon.Information, + DesktopNotifications.URGENCY_LOW) + + context.add_callback(_on_client_closed) + + # TODO: move to notifications.py + def new_node_message(): + print("new node connected, listening for client responses...", addr) + + while self._exit == False: + try: + if stop_event.is_set(): + break + in_message = next(node_iter) + if in_message == None: + continue + + self._nodes.reply_notification(addr, in_message) + except StopIteration: + print("[Notifications] Node {0} exited".format(addr)) + break + except grpc.RpcError as e: + print("[Notifications] grpc exception new_node_message(): ", addr, in_message) + except Exception as e: + print("[Notifications] unexpected exception new_node_message(): ", addr, e, in_message) + + read_thread = Thread(target=new_node_message) + read_thread.daemon = True + read_thread.start() + + while self._exit == False: + if stop_event.is_set(): + break + + try: + noti = _node['notifications'].get() + if noti != None: + _node['notifications'].task_done() + yield noti + except Exception as e: + print("[Notifications] exception getting notification from queue:", addr, e) + context.cancel() + + return node_iter diff --git a/ui/opensnitch/utils/__init__.py b/ui/opensnitch/utils/__init__.py new file mode 100644 index 0000000..4d81e48 --- /dev/null +++ b/ui/opensnitch/utils/__init__.py @@ -0,0 +1,507 @@ + +from PyQt5 import QtCore, QtWidgets, QtGui +from opensnitch.version import version as gui_version +from opensnitch.database import Database +from opensnitch.config import Config +from threading import Thread, Event +import pwd +import socket +import fcntl +import struct +import array +import os, sys, glob +import enum +import re + +class AsnDB(): + __instance = None + asndb = None + + @staticmethod + def instance(): + if AsnDB.__instance == None: + AsnDB.__instance = AsnDB() + return AsnDB.__instance + + def __init__(self): + self.ASN_AVAILABLE = True + self.load() + + def is_available(self): + return self.ASN_AVAILABLE + + def load(self): + """Load the ASN DB from disk. + + It'll try to load it from user's opensnitch directory if these file exist: + - ~/.config/opensnitch/ipasn_db.dat.gz + - ~/.config/opensnitch/asnames.json + Otherwise it'll try to load it from python3-pyasn package. + """ + try: + if self.asndb != None: + return + + import pyasn + + IPASN_DB_PATH = os.path.expanduser('~/.config/opensnitch/ipasn_db.dat.gz') + # .gz not supported for asnames + AS_NAMES_FILE_PATH = os.path.expanduser('~/.config/opensnitch/asnames.json') + + # if the user hasn't downloaded an updated ipasn db, use the one + # shipped with the python3-pyasn package + if os.path.isfile(IPASN_DB_PATH) == False: + IPASN_DB_PATH = '/usr/lib/python3/dist-packages/data/ipasn_20140513_v12.dat.gz' + if os.path.isfile(AS_NAMES_FILE_PATH) == False: + AS_NAMES_FILE_PATH = '/usr/lib/python3/dist-packages/data/asnames.json' + + print("using IPASN DB:", IPASN_DB_PATH) + self.asndb = pyasn.pyasn(IPASN_DB_PATH, as_names_file=AS_NAMES_FILE_PATH) + except Exception as e: + self.ASN_AVAILABLE = False + print("exception loading ipasn db:", e) + print("Install python3-pyasn to display IP's network name.") + + + def lookup(self, ip): + """Lookup the IP in the ASN DB. + + Return the net range and the prefix if found, otherwise nothing. + """ + try: + return self.asndb.lookup(ip) + except Exception: + return "", "" + + def get_as_name(self, asn): + """Get the ASN name given a network range. + + Return the name of the network if found, otherwise nothing. + """ + try: + asname = self.asndb.get_as_name(asn) + if asname == None: + asname = "" + return asname + except Exception: + return "" + + def get_asn(self, ip): + try: + asn, prefix = self.lookup(ip) + return self.get_as_name(asn) + except Exception: + return "" + +class Themes(): + """Change GUI's appearance using qt-material lib. + https://github.com/UN-GCPDS/qt-material + """ + THEMES_PATH = [ + os.path.expanduser("~/.config/opensnitch/"), + os.path.dirname(sys.modules[__name__].__file__) + ] + __instance = None + + AVAILABLE = False + try: + from qt_material import apply_stylesheet as qtmaterial_apply_stylesheet + from qt_material import list_themes as qtmaterial_themes + AVAILABLE = True + except Exception: + print("Themes not available. Install qt-material if you want to change GUI's appearance: pip3 install qt-material.") + + @staticmethod + def instance(): + if Themes.__instance == None: + Themes.__instance = Themes() + return Themes.__instance + + def __init__(self): + self._cfg = Config.get() + theme = self._cfg.getInt(self._cfg.DEFAULT_THEME, 0) + + def available(self): + return Themes.AVAILABLE + + def get_saved_theme(self): + theme = self._cfg.getSettings(self._cfg.DEFAULT_THEME) + theme_density = self._cfg.getSettings(self._cfg.DEFAULT_THEME_DENSITY_SCALE) + if theme_density == "" or theme_density == None: + theme_density = '0' + + if not Themes.AVAILABLE: + return 0, "", theme_density + + if theme != "" and theme != None: + # 0 == System + return self.list_themes().index(theme)+1, theme, theme_density + return 0, "", theme_density + + def save_theme(self, theme_idx, theme, density_scale): + if not Themes.AVAILABLE: + return + + self._cfg.setSettings(self._cfg.DEFAULT_THEME_DENSITY_SCALE, density_scale) + if theme_idx == 0: + self._cfg.setSettings(self._cfg.DEFAULT_THEME, "") + else: + self._cfg.setSettings(self._cfg.DEFAULT_THEME, theme) + + def load_theme(self, app): + if not Themes.AVAILABLE: + return + + try: + theme_idx, theme_name, theme_density = self.get_saved_theme() + if theme_name != "": + invert = "light" in theme_name + print("Using theme:", theme_idx, theme_name, "inverted:", invert) + # TODO: load {theme}.xml.extra and .xml.css for further + # customizations. + extra_opts = { + 'density_scale': theme_density + } + Themes.qtmaterial_apply_stylesheet(app, theme=theme_name, invert_secondary=invert, extra=extra_opts) + except Exception as e: + print("Themes.load_theme() exception:", e) + + def change_theme(self, window, theme_name, extra={}): + try: + invert = "light" in theme_name + Themes.qtmaterial_apply_stylesheet(window, theme=theme_name, invert_secondary=invert, extra=extra) + except Exception as e: + print("Themes.change_theme() exception:", e, " - ", window, theme_name) + + def list_local_themes(self): + themes = [] + if not Themes.AVAILABLE: + return themes + + try: + for tdir in self.THEMES_PATH: + themes += glob.glob(tdir + "/themes/*.xml") + except Exception: + pass + finally: + return themes + + def list_themes(self): + themes = self.list_local_themes() + if not Themes.AVAILABLE: + return themes + + themes += Themes.qtmaterial_themes() + return themes + +class GenericTimer(Thread): + interval = 1 + stop_flag = None + callback = None + + def __init__(self, _interval, _callback, _args=()): + Thread.__init__(self, name="generic_timer_thread") + self.interval = _interval + self.stop_flag = Event() + self.callback = _callback + self.args = _args + + def run(self): + while self.stop_flag.wait(self.interval): + if self.stop_flag.is_set(): + self.callback(self.args) + break + + def stop(self): + self.stop_flag.set() + +class OneshotTimer(GenericTimer): + def __init__(self, _interval, _callback, _args=()): + GenericTimer.__init__(self, _interval, _callback, _args) + + def run(self): + self.stop_flag.wait(self.interval) + self.callback(self.args) + +class CleanerTask(Thread): + interval = 1 + stop_flag = None + callback = None + + def __init__(self, _interval, _callback): + Thread.__init__(self, name="cleaner_db_thread") + self.interval = _interval * 60 + self.stop_flag = Event() + self.callback = _callback + self._cfg = Config.init() + + # We need to instantiate a new QsqlDatabase object with a unique name, + # because it's not thread safe: + # "A connection can only be used from within the thread that created it." + # https://doc.qt.io/qt-5/threads-modules.html#threads-and-the-sql-module + # The filename and type is the same, the one chosen by the user. + self.db = Database("db-cleaner-connection") + self.db_status, db_error = self.db.initialize( + dbtype=self._cfg.getInt(self._cfg.DEFAULT_DB_TYPE_KEY), + dbfile=self._cfg.getSettings(self._cfg.DEFAULT_DB_FILE_KEY), + dbjrnl_wal=self._cfg.getBool(self._cfg.DEFAULT_DB_JRNL_WAL) + ) + + def run(self): + if self.db_status == False: + return + while not self.stop_flag.is_set(): + self.stop_flag.wait(self.interval) + self.callback(self.db) + + def stop(self): + self.stop_flag.set() + self.db.close() + +class QuickHelp(): + @staticmethod + def show(help_str): + QtWidgets.QToolTip.showText(QtGui.QCursor.pos(), help_str) + +class Utils(): + @staticmethod + def check_versions(daemon_version): + lMayor, lMinor, lPatch = gui_version.split(".", 2) + rMayor, rMinor, rPatch = daemon_version.split(".", 2) + return lMayor != rMayor or (lMayor == rMayor and lMinor != rMinor) + + @staticmethod + def get_user_id(uid): + pw_name = uid + try: + pw_name = pwd.getpwuid(int(uid)).pw_name + " (" + uid + ")" + except Exception: + #pw_name += " (error)" + pass + + return pw_name + + @staticmethod + def get_interfaces(): + max_possible = 128 # arbitrary. raise if needed. + bytes = max_possible * 32 + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + names = array.array('B', b'\0' * bytes) + outbytes = struct.unpack('iL', fcntl.ioctl( + s.fileno(), + 0x8912, # SIOCGIFCONF + struct.pack('iL', bytes, names.buffer_info()[0]) + ))[0] + return names.tobytes(), outbytes + + @staticmethod + def create_socket_dirs(): + """https://www.linuxbase.org/betaspecs/fhs/fhs.html#runRuntimeVariableData + """ + run_path = "/run/user/{0}".format(os.getuid()) + var_run_path = "/var{0}".format(run_path) + + try: + if os.path.exists(run_path): + os.makedirs(run_path + "/opensnitch/", 0o700) + if os.path.exists(var_run_path): + os.makedirs(var_run_path + "/opensnitch/", 0o700) + except: + pass + +class Message(): + + @staticmethod + def ok(title, message, icon): + msgBox = QtWidgets.QMessageBox() + msgBox.setWindowFlags(msgBox.windowFlags() | QtCore.Qt.WindowStaysOnTopHint) + msgBox.setText("<b>{0}</b><br><br>{1}".format(title, message)) + msgBox.setIcon(icon) + msgBox.setModal(True) + msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok) + msgBox.exec_() + + @staticmethod + def yes_no(title, message, icon): + msgBox = QtWidgets.QMessageBox() + msgBox.setWindowFlags(msgBox.windowFlags() | QtCore.Qt.WindowStaysOnTopHint) + msgBox.setText(title) + msgBox.setIcon(icon) + msgBox.setModal(True) + msgBox.setInformativeText(message) + msgBox.setStandardButtons(QtWidgets.QMessageBox.Cancel | QtWidgets.QMessageBox.Yes) + msgBox.setDefaultButton(QtWidgets.QMessageBox.Cancel) + return msgBox.exec_() + +class FileDialog(): + + @staticmethod + def save(parent): + options = QtWidgets.QFileDialog.Options() + fileName, _ = QtWidgets.QFileDialog.getSaveFileName(parent, "", "","All Files (*)", options=options) + return fileName + + @staticmethod + def select(parent): + options = QtWidgets.QFileDialog.Options() + fileName, _ = QtWidgets.QFileDialog.getOpenFileName(parent, "", "","All Files (*)", options=options) + return fileName + + @staticmethod + def select_dir(parent, current_dir): + options = QtWidgets.QFileDialog.Options() + fileName = QtWidgets.QFileDialog.getExistingDirectory(parent, "", current_dir, options) + return fileName + +# https://stackoverflow.com/questions/29503339/how-to-get-all-values-from-python-enum-class +class Enums(enum.Enum): + @classmethod + def to_dict(cls): + return {e.name: e.value for e in cls} + + @classmethod + def keys(cls): + return cls._member_names_ + + @classmethod + def values(cls): + return [str(v.value) for v in cls] + +class NetworkInterfaces(): + # https://gist.github.com/pklaus/289646 + @staticmethod + def list(): + namestr, outbytes = Utils.get_interfaces() + _interfaces = {} + for i in range(0, outbytes, 40): + try: + name = namestr[i:i+16].split(b'\0', 1)[0] + addr = namestr[i+20:i+24] + _interfaces[name.decode()] = "%d.%d.%d.%d" % (int(addr[0]), int(addr[1]), int(addr[2]), int(addr[3])) + except Exception as e: + print("utils.NetworkInterfaces() exception:", e) + + return _interfaces + + + +class NetworkServices(): + """Get a list of known ports. /etc/services + """ + __instance = None + + @staticmethod + def instance(): + if NetworkServices.__instance == None: + NetworkServices.__instance = NetworkServices() + return NetworkServices.__instance + + srv_array = [] + ports_list = [] + + def __init__(self): + etcServicesPath = "/etc/services" + if not os.path.isfile(etcServicesPath) and os.path.isfile("/usr/etc/services"): + etcServicesPath = "/usr/etc/services" + + try: + etcServices = open(etcServicesPath) + for line in etcServices: + if line[0] == "#": + continue + g = re.search(r'([a-zA-Z0-9\-]+)( |\t)+([0-9]+)\/([a-zA-Z0-9\-]+)(.*)\n', line) + if g: + self.srv_array.append("{0}/{1} {2}".format( + g.group(1), + g.group(3), + "" if len(g.groups())>3 and g.group(4) == "" else "({0})".format(g.group(4).replace("\t", "")) + ) + ) + self.ports_list.append(g.group(3)) + + # extra ports that don't exist in /etc/services + self.srv_array.append("wireguard/51820 WireGuard VPN") + self.ports_list.append("51820") + except Exception as e: + print("Error loading {0}: {1}".format(etcServicesPath, e)) + + def to_array(self): + return self.srv_array + + def service_by_index(self, idx): + return self.srv_array[idx] + + def service_by_name(self, name): + return self.srv_array.index(name) + + def port_by_index(self, idx): + return self.ports_list[idx] + + def index_by_port(self, port): + return self.ports_list.index(str(port)) + +class Icons(): + """Util to display Qt's built-in icons when the system is not configured as + we expect. More information: + https://github.com/evilsocket/opensnitch/wiki/GUI-known-problems#no-icons-on-the-gui + https://www.pythonguis.com/faq/built-in-qicons-pyqt/icons-builtin.png + """ + + defaults = { + 'document-new': "SP_FileIcon", + 'document-save': "SP_DialogSaveButton", + 'document-open': "SP_DirOpenIcon", + 'format-justify-fill': "SP_FileDialogDetailedView", + 'preferences-system': "SP_FileDialogListView", + 'preferences-desktop': "SP_FileDialogListView", + 'security-high': "SP_VistaShield", + 'go-previous': "SP_ArrowLeft", + 'go-jump': "SP_CommandLink", + 'go-down': "SP_TitleBarUnshadeButton", + 'go-up': "SP_TitleBarShadeButton", + 'help-browser': "SP_DialogHelpButton", + 'emblem-important': "SP_DialogCancelButton", + 'emblem-default': "SP_DialogApplyButton", + 'window-close': "SP_DialogCloseButton", + 'system-run': "", + 'preferences-system-network': "", + 'document-properties': "", + 'edit-delete': "SP_DialogCancelButton", + 'list-add': "SP_ArrowUp", + 'list-remove': "SP_ArrowDown", + 'system-search': "SP_FileDialogContentsView", + 'application-exit': "SP_TitleBarCloseButton", + 'view-sort-ascending': "SP_ToolBarVerticalExtensionButton", + 'address-book-new': "", + 'media-playback-start': "SP_MediaPlay", + 'media-playback-pause': "SP_MediaPause", + 'system-search': "SP_FileDialogContentsView", + 'accessories-text-editor': "SP_DialogOpenButton", + 'edit-clear-all': "SP_DialogResetButton", + 'reload': "SP_DialogResetButton", + 'dialog-information': "SP_MessageBoxInformation", + 'dialog-warning': "SP_MessageBoxWarning" + } + + @staticmethod + def new(widget, icon_name): + icon = QtGui.QIcon.fromTheme(icon_name, QtGui.QIcon.fromTheme(icon_name + "-symbolic")) + if icon.isNull(): + try: + return widget.style().standardIcon(getattr(QtWidgets.QStyle, Icons.defaults[icon_name])) + except Exception as e: + print("Qt standardIcon exception:", icon_name, ",", e) + + return icon + +class Versions(): + @staticmethod + def get(): + try: + from google.protobuf import __version__ as proto_version + from grpc import _grpcio_metadata as grpcmeta + + return gui_version, grpcmeta.__version__, proto_version + + except: + return "none", "none", "none" diff --git a/ui/opensnitch/utils/infowindow.py b/ui/opensnitch/utils/infowindow.py new file mode 100644 index 0000000..478ec6e --- /dev/null +++ b/ui/opensnitch/utils/infowindow.py @@ -0,0 +1,54 @@ + +from opensnitch.config import Config +from PyQt5 import QtCore, QtWidgets, QtGui + +class InfoWindow(QtWidgets.QDialog): + """Display a text on a small dialog. + """ + def __init__(self, parent): + QtWidgets.QDialog.__init__(self, parent, QtCore.Qt.Tool) + self.setContentsMargins(0, 0, 0, 0) + + self._cfg = Config.get() + + self.layout = QtWidgets.QVBoxLayout(self) + self._textedit = QtWidgets.QTextEdit() + # hide cursor + self._textedit.setCursorWidth(0) + self._textedit.setMinimumSize(300, 325) + self._textedit.setReadOnly(True) + self._textedit.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse | QtCore.Qt.TextSelectableByKeyboard) + + self.layout.addWidget(self._textedit) + + self._load_settings() + + def closeEvent(self, ev): + self._save_settings() + ev.accept() + self.hide() + + def _load_settings(self): + saved_geometry = self._cfg.getSettings(Config.INFOWIN_GEOMETRY) + if saved_geometry is not None: + self.restoreGeometry(saved_geometry) + + def _save_settings(self): + self._cfg.setSettings(Config.INFOWIN_GEOMETRY, self.saveGeometry()) + + def showText(self, text): + self._load_settings() + + self._textedit.setText(text) + + pos = QtGui.QCursor.pos() + win_size = self.size() + # center dialog on cursor, relative to the parent widget. + x_off = (int(win_size.width()/2)) + y_off = (int(win_size.height()/2)) + point = QtCore.QPoint( + pos.x()-x_off, pos.y()-y_off + ) + self.move(point.x(), point.y()) + + self.show() diff --git a/ui/opensnitch/utils/languages.py b/ui/opensnitch/utils/languages.py new file mode 100644 index 0000000..90f4a78 --- /dev/null +++ b/ui/opensnitch/utils/languages.py @@ -0,0 +1,36 @@ +from PyQt5 import QtCore +import os + +from opensnitch.config import Config + +def __get_i18n_path(): + return os.path.dirname(os.path.realpath(__file__)) + "/../i18n" + +def init(saved_lang): + locale = QtCore.QLocale.system() + lang = locale.name() + if saved_lang: + lang = saved_lang + i18n_path = __get_i18n_path() + print("Loading translations:", i18n_path, "locale:", lang) + translator = QtCore.QTranslator() + translator.load(i18n_path + "/" + lang + "/opensnitch-" + lang + ".qm") + + return translator + +def save(cfg, lang): + q = QtCore.QLocale(lang) + cfg.setSettings(Config.DEFAULT_LANGUAGE, lang) + cfg.setSettings(Config.DEFAULT_LANGNAME, q.nativeLanguageName().capitalize()) + +def get_all(): + langs = [] + names = [] + i18n_path = __get_i18n_path() + lang_dirs = os.listdir(i18n_path) + lang_dirs.sort() + for lang in lang_dirs: + q = QtCore.QLocale(lang) + langs.append(lang) + names.append(q.nativeLanguageName()) + return langs, names diff --git a/ui/opensnitch/utils/qvalidator.py b/ui/opensnitch/utils/qvalidator.py new file mode 100644 index 0000000..881d4e6 --- /dev/null +++ b/ui/opensnitch/utils/qvalidator.py @@ -0,0 +1,25 @@ + +from PyQt5 import QtCore, QtGui + +class RestrictChars(QtGui.QValidator): + result = QtCore.pyqtSignal(object) + + def __init__(self, restricted_chars, *args, **kwargs): + QtGui.QValidator.__init__(self, *args, **kwargs) + self._restricted_chars = restricted_chars + + def validate(self, value, pos): + # allow to delete all characters + if len(value) == 0: + return QtGui.QValidator.Intermediate, value, pos + + # user can type characters or paste them. + # pos value when pasting can be any number, depending on where did the + # user paste the characters. + for char in self._restricted_chars: + if char in value: + self.result.emit(QtGui.QValidator.Invalid) + return QtGui.QValidator.Invalid, value, pos + + self.result.emit(QtGui.QValidator.Acceptable) + return QtGui.QValidator.Acceptable, value, pos diff --git a/ui/opensnitch/utils/xdg.py b/ui/opensnitch/utils/xdg.py new file mode 100644 index 0000000..6158f37 --- /dev/null +++ b/ui/opensnitch/utils/xdg.py @@ -0,0 +1,117 @@ + +import os +import re +import shutil +import stat + +# https://github.com/takluyver/pyxdg/blob/1d23e483ae869ee9532aca43b133cc43f63626a3/xdg/BaseDirectory.py +def get_runtime_dir(strict=True): + try: + return os.environ['XDG_RUNTIME_DIR'] + except KeyError: + if strict: + raise + + import getpass + fallback = '/tmp/opensnitch-' + getpass.getuser() + create = False + + try: + # This must be a real directory, not a symlink, so attackers can't + # point it elsewhere. So we use lstat to check it. + st = os.lstat(fallback) + except OSError as e: + import errno + if e.errno == errno.ENOENT: + create = True + else: + raise + else: + # The fallback must be a directory + if not stat.S_ISDIR(st.st_mode): + os.unlink(fallback) + create = True + # Must be owned by the user and not accessible by anyone else + elif (st.st_uid != os.getuid()) \ + or (st.st_mode & (stat.S_IRWXG | stat.S_IRWXO)): + os.rmdir(fallback) + create = True + + if create: + os.mkdir(fallback, 0o700) + + return fallback + +def get_run_opensnitch_dir(): + rdir = get_runtime_dir(False) + if 'opensnitch' not in rdir: + rdir = os.path.join(rdir, 'opensnitch') + try: + os.makedirs(rdir, 0o700) + except: + pass + + return rdir + + +class Autostart(): + def __init__(self): + desktopFile = 'opensnitch_ui.desktop' + self.systemDesktop = os.path.join('/usr/share/applications', desktopFile) + self.systemAutostart = os.path.join('/etc/xdg/autostart', desktopFile) + if not os.path.isfile(self.systemAutostart) and os.path.isfile('/usr' + self.systemAutostart): + self.systemAutostart = '/usr' + self.systemAutostart + self.userAutostart = os.path.join(xdg_config_home, 'autostart', desktopFile) + + def _copyfile(self, src, dst): + """copy file (.desktop) to dst. Ignore exception if src and dst are equal""" + try: + shutil.copyfile(src, dst) + except shutil.SameFileError: + pass + + def createUserDir(self): + if not os.path.isdir(xdg_config_home): + os.makedirs(xdg_config_home, 0o700) + if not os.path.isdir(os.path.dirname(self.userAutostart)): + os.makedirs(os.path.dirname(self.userAutostart), 0o755) + + def isEnabled(self): + ret = False + if os.path.isfile(self.userAutostart): + ret = True + lines = open(self.userAutostart, 'r').readlines() + for line in lines: + if re.search("^Hidden=true", line, re.IGNORECASE): + ret = False + break + elif os.path.isfile(self.systemAutostart): + ret = True + return ret + + def enable(self, mode=True): + self.createUserDir() + if mode == True: + if os.path.isfile(self.systemAutostart) and os.path.isfile(self.userAutostart): + os.remove(self.userAutostart) + elif os.path.isfile(self.systemDesktop): + self._copyfile(self.systemDesktop, self.userAutostart) + else: + if os.path.isfile(self.systemAutostart): + self._copyfile(self.systemAutostart, self.userAutostart) + with open(self.userAutostart, 'a') as f: + f.write('Hidden=true\n') + elif os.path.isfile(self.userAutostart): + os.remove(self.userAutostart) + + def disable(self): + self.enable(False) + + +_home = os.path.expanduser('~') +xdg_config_home = os.environ.get('XDG_CONFIG_HOME') or os.path.join(_home, '.config') +xdg_runtime_dir = get_runtime_dir(False) +xdg_current_desktop = os.environ.get('XDG_CURRENT_DESKTOP') +xdg_current_session = os.environ.get('XDG_SESSION_TYPE') + +xdg_opensnitch_dir = get_run_opensnitch_dir() diff --git a/ui/opensnitch/version.py b/ui/opensnitch/version.py new file mode 100644 index 0000000..c042c67 --- /dev/null +++ b/ui/opensnitch/version.py @@ -0,0 +1 @@ +version = '1.6.9' diff --git a/ui/requirements.txt b/ui/requirements.txt new file mode 100644 index 0000000..66e0de1 --- /dev/null +++ b/ui/requirements.txt @@ -0,0 +1,5 @@ +grpcio-tools>=1.10.1 +pyinotify==0.9.6 +unicode_slugify==0.1.5 +pyqt5>=5.6 +protobuf diff --git a/ui/resources/icons/48x48/opensnitch-ui.png b/ui/resources/icons/48x48/opensnitch-ui.png new file mode 100644 index 0000000..6dcbd5f Binary files /dev/null and b/ui/resources/icons/48x48/opensnitch-ui.png differ diff --git a/ui/resources/icons/64x64/opensnitch-ui.png b/ui/resources/icons/64x64/opensnitch-ui.png new file mode 100644 index 0000000..4b73e6b Binary files /dev/null and b/ui/resources/icons/64x64/opensnitch-ui.png differ diff --git a/ui/resources/icons/opensnitch-ui.svg b/ui/resources/icons/opensnitch-ui.svg new file mode 100644 index 0000000..df12789 --- /dev/null +++ b/ui/resources/icons/opensnitch-ui.svg @@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + width="64" + height="64" + viewBox="0 0 12.698413 12.698413" + version="1.1" + id="svg8" + inkscape:version="1.2.2 (b0a8486541, 2022-12-01)" + sodipodi:docname="opensnitch-ui.svg" + inkscape:export-filename="64x64/opensnitch-ui.png" + inkscape:export-xdpi="96" + inkscape:export-ydpi="96" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:dc="http://purl.org/dc/elements/1.1/"> + <defs + id="defs2" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="9.75" + inkscape:cx="21.74359" + inkscape:cy="31.794872" + inkscape:document-units="px" + inkscape:current-layer="g4133" + showgrid="true" + inkscape:window-width="1600" + inkscape:window-height="839" + inkscape:window-x="0" + inkscape:window-y="24" + inkscape:window-maximized="1" + units="px" + inkscape:pagecheckerboard="0" + inkscape:showpageshadow="2" + inkscape:deskcolor="#d1d1d1" + showguides="true"> + <sodipodi:guide + position="13.436869,10.958536" + orientation="0,-1" + id="guide291" + inkscape:locked="false" /> + <sodipodi:guide + position="13.233163,1.6160318" + orientation="0,-1" + id="guide293" + inkscape:locked="false" /> + <inkscape:grid + type="xygrid" + id="grid295" /> + </sodipodi:namedview> + <metadata + id="metadata5"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Capa 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(0,-284.30001)"> + <g + id="g4133" + transform="matrix(0.9182611,0,0,0.9182611,-19.582232,24.4642)"> + <path + style="fill:#232629;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.287462px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 23.93778,289.55608 0.922529,-2.47895 2.563056,-0.57549 2.395943,-1.27803 2.491924,1.17578 0.401143,2.50266 1.620734,1.26491 0.381256,1.78538 -0.666013,1.62872 -1.326357,0.85434 -9.105041,0.0948 -1.530218,-1.13976 -0.31061,-1.71767 0.780558,-1.56852 z" + id="path1481" + sodipodi:nodetypes="ccccccccccccccc" + inkscape:export-filename="/home/ga/Proiektuak/opensnitch/gui/opensnitch/ui/lib/python3.9/site-packages/opensnitch/res/icon-white.png" + inkscape:export-xdpi="96" + inkscape:export-ydpi="96" /> + <path + style="fill:#fbffff;fill-opacity:1;stroke:none;stroke-width:0.0267051;stroke-opacity:1" + d="m 23.924347,295.01517 c -1.190403,-0.17151 -2.160364,-1.04049 -2.48066,-2.22233 -0.06228,-0.22986 -0.06956,-0.30524 -0.06956,-0.7212 0,-0.41596 0.0072,-0.49134 0.06956,-0.72117 0.147237,-0.54329 0.408851,-0.99654 0.795944,-1.37896 0.324557,-0.32066 0.785409,-0.60618 1.154881,-0.71557 l 0.103224,-0.0303 0.01629,-0.29557 c 0.07143,-1.29674 0.972433,-2.37494 2.27078,-2.71736 0.229851,-0.0607 0.312438,-0.0688 0.716539,-0.0696 0.257818,-6.1e-4 0.509198,0.0123 0.577176,0.0292 0.120416,0.0303 0.120441,0.0303 0.198793,-0.0679 0.155946,-0.19524 0.509186,-0.51209 0.74373,-0.66706 0.551463,-0.36443 1.11147,-0.54697 1.774575,-0.57842 0.589067,-0.0277 1.121425,0.0806 1.650413,0.33595 0.72776,0.35149 1.243577,0.8611 1.599369,1.58011 0.255831,0.51699 0.367014,1.04428 0.341349,1.61884 l -0.01332,0.2989 0.217937,0.14177 c 0.119863,0.078 0.346565,0.26788 0.503782,0.42196 0.88975,0.87202 1.218873,2.08704 0.893766,3.29955 -0.132616,0.49457 -0.500547,1.11835 -0.881945,1.49513 -0.364404,0.36002 -1.001057,0.73363 -1.4664,0.86052 -0.492709,0.13439 -0.431399,0.13271 -4.634625,0.12938 -2.15745,-0.002 -3.994154,-0.0135 -4.081564,-0.0261 z m 8.365743,-0.89453 c 0.59725,-0.15778 1.128615,-0.51689 1.486943,-1.00491 0.147801,-0.20133 0.33126,-0.60206 0.39973,-0.87313 0.08448,-0.33442 0.08466,-0.85621 4.07e-4,-1.18961 -0.203279,-0.80452 -0.831632,-1.50173 -1.597272,-1.77233 -0.153083,-0.0541 -0.232971,-0.0955 -0.224805,-0.11652 0.252703,-0.65059 0.224901,-1.36673 -0.07754,-1.99749 -0.264858,-0.55243 -0.669292,-0.9522 -1.225766,-1.21167 -0.399047,-0.18607 -0.636305,-0.23736 -1.097982,-0.23736 -0.319298,0 -0.430775,0.0109 -0.61795,0.0601 -0.739241,0.19424 -1.358854,0.68643 -1.680928,1.33522 l -0.07478,0.15063 -0.122961,-0.0614 c -0.228775,-0.11421 -0.565847,-0.2025 -0.834911,-0.21871 -0.932118,-0.0562 -1.838882,0.55372 -2.13689,1.4371 -0.168757,0.50024 -0.151725,0.9779 0.05358,1.50243 0.01377,0.035 -0.0089,0.0394 -0.156728,0.03 -0.208357,-0.0132 -0.547143,0.0503 -0.81323,0.15241 -0.54704,0.20974 -1.043227,0.72489 -1.228933,1.27596 -0.172171,0.51087 -0.161028,0.97577 0.03539,1.47692 0.212384,0.54181 0.73321,1.03044 1.29576,1.21563 0.113249,0.0371 0.270424,0.0783 0.349276,0.0916 0.08114,0.0136 1.857609,0.0221 4.092874,0.0195 l 3.949506,-0.004 z m -5.761885,-1.40118 c -0.576304,-0.34169 -1.047827,-0.63319 -1.047827,-0.64782 0,-0.0148 0.471523,-0.30613 1.047827,-0.64783 l 1.047829,-0.62116 0.0074,0.42212 0.0074,0.42218 h 1.718841 1.718847 v 0.42469 0.42469 h -1.718876 -1.718849 l -0.0074,0.42218 -0.0074,0.42217 z m 2.350898,-2.34355 v -0.42777 h -1.719512 -1.719514 v -0.42468 -0.42471 h 1.718847 1.718849 l 0.0074,-0.42217 0.0074,-0.42216 1.047812,0.6212 c 0.5763,0.34168 1.051205,0.63124 1.055344,0.64353 0.0041,0.0124 -0.443196,0.28902 -0.99408,0.61507 -0.550883,0.32599 -1.028812,0.61 -1.062059,0.63111 l -0.06046,0.0382 z" + id="path826-6" + inkscape:connector-curvature="0" /> + </g> + </g> +</svg> diff --git a/ui/resources/io.github.evilsocket.opensnitch.appdata.xml b/ui/resources/io.github.evilsocket.opensnitch.appdata.xml new file mode 100644 index 0000000..b55aff9 --- /dev/null +++ b/ui/resources/io.github.evilsocket.opensnitch.appdata.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component type="desktop-application"> + <id>io.github.evilsocket.opensnitch</id> + + <name>OpenSnitch</name> + <summary>GNU/Linux interactive application firewall</summary> + + <metadata_license>FTL</metadata_license> + <project_license>GPL-3.0-or-later</project_license> + + <supports> + <control>pointing</control> + <control>keyboard</control> + <control>touch</control> + </supports> + + <description> + <p> + Whenever a program tries to establish a new connection, it'll prompt the user to allow or deny it. + </p> + <p> + The user can decide if block the outgoing connection based on properties of the connection: by port, by uid, by dst ip, by program or a combination of them. These rules can last forever, until the app restart or just one time. + </p> + <p> + The GUI allows the user to view live outgoing connections, as well as search by process, user, host or port. + </p> + <p> + OpenSnitch can also work as a system-wide domains blocker, by using lists of domains, list of IPs or list of regular expressions. + </p> + </description> + + <categories> + <category>System</category> + <category>Security</category> + <category>Monitor</category> + <category>Network</category> + </categories> + + <icon type="stock">opensnitch-ui</icon> + <url type="homepage">https://github.com/evilsocket/opensnitch</url> + <url type="bugtracker">https://github.com/evilsocket/opensnitch/issues</url> + <url type="help">https://github.com/evilsocket/opensnitch/wiki</url> + <launchable type="desktop-id">opensnitch_ui.desktop</launchable> + <screenshots> + <screenshot type="default"> + <image>https://user-images.githubusercontent.com/2742953/85205382-6ba9cb00-b31b-11ea-8e9a-bd4b8b05a236.png</image> + </screenshot> + <screenshot> + <image>https://user-images.githubusercontent.com/2742953/217039798-3477c6c2-d64f-4eea-89af-cd94ee77cff4.png</image> + </screenshot> + <screenshot> + <image>https://user-images.githubusercontent.com/2742953/99863173-3987e800-2b9d-11eb-93f2-fe3121b18c51.png</image> + </screenshot> + </screenshots> + <content_rating type="oars-1.0" /> +</component> diff --git a/ui/resources/kcm_opensnitch.desktop b/ui/resources/kcm_opensnitch.desktop new file mode 100644 index 0000000..ee2c112 --- /dev/null +++ b/ui/resources/kcm_opensnitch.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Exec=opensnitch-ui +Icon=opensnitch-ui +Type=Service +X-KDE-ServiceTypes=SystemSettingsExternalApp +Name=OpenSnitch Firewall +Comment=OpenSnitch Firewall Graphical Interface +X-KDE-Keywords=system,firewall,policies,security,polkit,policykit,douane +X-KDE-Autostart-after=panel diff --git a/ui/resources/opensnitch_ui.desktop b/ui/resources/opensnitch_ui.desktop new file mode 100644 index 0000000..782309f --- /dev/null +++ b/ui/resources/opensnitch_ui.desktop @@ -0,0 +1,18 @@ +[Desktop Entry] +Type=Application +Name=OpenSnitch +Exec=opensnitch-ui +Icon=opensnitch-ui +GenericName=OpenSnitch Firewall +GenericName[hu]=OpenSnitch-tűzfal +GenericName[nb]=OpenSnitch brannmur +Comment=Interactive application firewall +Comment[es]=Firewall de aplicaciones +Comment[hu]=Alkalmazási tűzfal +Comment[nb]=Interaktiv programbrannmur +Terminal=false +NoDisplay=false +Categories=System;Security;Monitor;Network; +Keywords=system;firewall;policies;security;polkit;policykit; +X-GNOME-Autostart-Delay=3 +X-GNOME-Autostart-enabled=true diff --git a/ui/setup.py b/ui/setup.py new file mode 100644 index 0000000..ee11d73 --- /dev/null +++ b/ui/setup.py @@ -0,0 +1,38 @@ +from setuptools import setup, find_packages + +import os +import sys + +path = os.path.abspath(os.path.dirname(__file__)) +sys.path.append(path) + +from opensnitch.version import version + +setup(name='opensnitch-ui', + version=version, + description='Prompt service and UI for the opensnitch interactive firewall application.', + long_description='GUI for the opensnitch interactive firewall application\n\ +opensnitch-ui is a GUI for opensnitch written in Python.\n\ +It allows the user to view live outgoing connections, as well as search\n\ +to make connections.\n\ +.\n\ +The user can decide if block the outgoing connection based on properties of\n\ +the connection: by port, by uid, by dst ip, by program or a combination\n\ +of them.\n\ +.\n\ +These rules can last forever, until the app restart or just one time.', + url='https://github.com/evilsocket/opensnitch', + author='Simone "evilsocket" Margaritelli', + author_email='evilsocket@protonmail.com', + license='GPL-3.0', + packages=find_packages(), + include_package_data = True, + package_data={'': ['*.*']}, + data_files=[('/usr/share/applications', ['resources/opensnitch_ui.desktop']), + ('/usr/share/kservices5', ['resources/kcm_opensnitch.desktop']), + ('/usr/share/icons/hicolor/scalable/apps', ['resources/icons/opensnitch-ui.svg']), + ('/usr/share/icons/hicolor/48x48/apps', ['resources/icons/48x48/opensnitch-ui.png']), + ('/usr/share/icons/hicolor/64x64/apps', ['resources/icons/64x64/opensnitch-ui.png']), + ('/usr/share/metainfo', ['resources/io.github.evilsocket.opensnitch.appdata.xml'])], + scripts = [ 'bin/opensnitch-ui' ], + zip_safe=False) diff --git a/ui/tests/README.md b/ui/tests/README.md new file mode 100644 index 0000000..e94453b --- /dev/null +++ b/ui/tests/README.md @@ -0,0 +1,23 @@ +GUI unit tests. + +We use pytest [0] to pytest-qt [1] to test GUI code. + +To run the tests: `cd tests; pytest -v` + +TODO: + - test service class (Service.py) + - test events window (stats.py): + - The size of the window must be saved on close, and restored when opening it again. + - Columns width of every view must be saved and restored properly. + - On the Events tab, clicking on the Node, Process or Rule column must jump to the detailed view of the selected item. + - When entering into a detail view: + - the results limit configured must be respected (that little button on the bottom right of every tab). + - must apply the proper SQL query for every detailed view. + - When going back from a detail view: + - The SQL query must be restored. + - Test rules context menu actions. + - Test select rows and copy them to the clipboard (ctrl+c). + + +0. https://docs.pytest.org/en/6.2.x/ +1. https://pytest-qt.readthedocs.io/en/latest/intro.html diff --git a/ui/tests/__init__.py b/ui/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ui/tests/dialogs/__init__.py b/ui/tests/dialogs/__init__.py new file mode 100644 index 0000000..c562095 --- /dev/null +++ b/ui/tests/dialogs/__init__.py @@ -0,0 +1,54 @@ + +from opensnitch.database import Database +from opensnitch.config import Config +from opensnitch.nodes import Nodes + +# grpc object +class ClientConfig: + version = "1.2.3" + name = "bla" + logLevel = 0 + isFirewallRunning = False + rules = [] + config = '''{ + "Server":{ + "Address": "unix:///tmp/osui.sock", + "LogFile": "/var/log/opensnitchd.log" + }, + "DefaultAction": "deny", + "DefaultDuration": "once", + "InterceptUnknown": false, + "ProcMonitorMethod": "ebpf", + "LogLevel": 0, + "LogUTC": true, + "LogMicro": false, + "Firewall": "iptables", + "Stats": { + "MaxEvents": 150, + "MaxStats": 50 + } + } + ''' + +class Connection: + protocol = "tcp" + src_ip = "127.0.0.1" + src_port = "12345" + dst_ip = "127.0.0.1" + dst_host = "localhost" + dst_port = "54321" + user_id = 1000 + process_id = 9876 + process_path = "/bin/cmd" + process_cwd = "/tmp" + process_args = "/bin/cmd --parm1 test" + process_env = [] + +db = Database.instance() +db.initialize() +Config.init() + +nodes = Nodes.instance() +nodes._nodes["unix:/tmp/osui.sock"] = { + 'data': ClientConfig +} diff --git a/ui/tests/dialogs/test_preferences.py b/ui/tests/dialogs/test_preferences.py new file mode 100644 index 0000000..62607b1 --- /dev/null +++ b/ui/tests/dialogs/test_preferences.py @@ -0,0 +1,146 @@ +# +# pytest -v tests/dialogs/test_ruleseditor.py +# +import os +import time +import json +from PyQt5 import QtCore, QtWidgets, QtGui + +from opensnitch.config import Config +from opensnitch.dialogs.preferences import PreferencesDialog + +class TestPreferences(): + + @classmethod + def reset_settings(self): + try: + os.remove(os.environ['HOME'] + "/.config/opensnitch/settings.conf") + except Exception: + pass + + @classmethod + def setup_method(self): + white_icon = QtGui.QIcon("../res/icon-white.svg") + self.reset_settings() + self.prefs = PreferencesDialog(appicon=white_icon) + self.prefs.show() + + def run(self, qtbot): + def handle_dialog(): + qtbot.mouseClick(self.prefs.applyButton, QtCore.Qt.LeftButton) + qtbot.mouseClick(self.prefs.acceptButton, QtCore.Qt.LeftButton) + + QtCore.QTimer.singleShot(500, handle_dialog) + self.prefs.exec_() + + def test_save_popups_settings(self, qtbot): + """ Test saving UI related settings. + """ + qtbot.addWidget(self.prefs) + + self.prefs.comboUIAction.setCurrentIndex(Config.ACTION_ALLOW_IDX) + self.prefs.comboUITarget.setCurrentIndex(2) + self.prefs.comboUIDuration.setCurrentIndex(4) + self.prefs.comboUIDialogPos.setCurrentIndex(2) + self.prefs.spinUITimeout.setValue(30) + self.prefs.showAdvancedCheck.setChecked(True) + self.prefs.uidCheck.setChecked(True) + + self.run(qtbot) + + assert self.prefs._cfg.getInt(self.prefs._cfg.DEFAULT_ACTION_KEY) == Config.ACTION_ALLOW_IDX and self.prefs.comboUIAction.currentText() == Config.ACTION_ALLOW + assert self.prefs._cfg.getInt(self.prefs._cfg.DEFAULT_TARGET_KEY) == 2 + assert self.prefs._cfg.getInt(self.prefs._cfg.DEFAULT_DURATION_KEY) == 4 + assert self.prefs._cfg.getInt(self.prefs._cfg.DEFAULT_TIMEOUT_KEY) == 30 + assert self.prefs._cfg.getInt(self.prefs._cfg.DEFAULT_POPUP_POSITION) == 2 + assert self.prefs._cfg.getBool(self.prefs._cfg.DEFAULT_POPUP_ADVANCED) == True + assert self.prefs._cfg.getBool(self.prefs._cfg.DEFAULT_POPUP_ADVANCED_UID) == True + + def test_save_ui_settings(self, qtbot): + self.prefs.checkUIRules.setChecked(True) + self.prefs.comboUIRules.setCurrentIndex(1) + self.prefs.checkHideNode.setChecked(False) + self.prefs.checkHideProto.setChecked(False) + + self.run(qtbot) + + assert self.prefs._cfg.getBool(self.prefs._cfg.DEFAULT_IGNORE_RULES) == True and self.prefs._cfg.getInt(self.prefs._cfg.DEFAULT_IGNORE_TEMPORARY_RULES) == 1 + cols = self.prefs._cfg.getSettings(Config.STATS_SHOW_COLUMNS) + assert cols == ['0','2','3','5','6'] + + def test_save_node_settings(self, qtbot, capsys): + self.prefs.comboNodeAction.setCurrentIndex(Config.ACTION_ALLOW_IDX) + self.prefs.comboNodeMonitorMethod.setCurrentIndex(2) + self.prefs.comboNodeLogLevel.setCurrentIndex(5) + self.prefs.checkNodeLogUTC.setChecked(False) + self.prefs.checkNodeLogMicro.setChecked(True) + self.prefs.checkInterceptUnknown.setChecked(True) + self.prefs.tabWidget.setCurrentIndex(self.prefs.TAB_NODES) + self.prefs._node_needs_update = True + + self.run(qtbot) + + assert len(self.prefs._notifications_sent) == 1 + for n in self.prefs._notifications_sent: + conf = json.loads(self.prefs._notifications_sent[n].data) + assert conf['InterceptUnknown'] == True + assert conf['ProcMonitorMethod'] == "audit" + assert conf['LogLevel'] == 5 + assert conf['LogUTC'] == False + assert conf['LogMicro'] == True + assert conf['DefaultAction'] == "allow" + +# TODO: click on the QMessageDialog +# +# def test_save_db_settings(self, qtbot, monkeypatch, capsys): +# self.prefs.comboDBType.setCurrentIndex(1) +# self.prefs.dbLabel.setText('/tmp/test.db') +# +# def handle_dialog(): +# qtbot.mouseClick(self.prefs.applyButton, QtCore.Qt.LeftButton) +# # after saving the settings, a warning dialog must appear, informing +# # the user to restart the GUI +# time.sleep(.5) +# msgbox = QtWidgets.QApplication.activeModalWidget() +# try: +# assert msgbox != None +# okBtn = msgbox.button(QtWidgets.QMessageBox.Ok) +# qtbot.mouseClick(okBtn, QtCore.Qt.LeftButton) +# except Exception as e: +# print("test_save_db_Settings() exception:", e) +# qtbot.mouseClick(self.prefs.acceptButton, QtCore.Qt.LeftButton) +# +# QtCore.QTimer.singleShot(500, handle_dialog) +# self.prefs.exec_() + +# assert self.prefs._cfg.getInt(Config.DEFAULT_DB_TYPE_KEY) == 1 +# assert self.prefs._cfg.getSettings(Config.DEFAULT_DB_FILE_KEY) == '/tmp/test.db' + + def test_load_ui_settings(self, qtbot, capsys): + """ reTest saved settings (load_settings()). + On dialog show up the widgets must be configured properly, with the settings + configured in previous tests. + """ + self.prefs.checkUIRules.setChecked(False) + self.prefs.comboUIRules.setCurrentIndex(0) + self.prefs.comboUITarget.setCurrentIndex(0) + self.prefs.comboUIDuration.setCurrentIndex(0) + self.prefs.checkHideNode.setChecked(True) + self.prefs.checkHideProto.setChecked(True) + + def handle_dialog(): + qtbot.mouseClick(self.prefs.cancelButton, QtCore.Qt.LeftButton) + QtCore.QTimer.singleShot(500, handle_dialog) + + self.prefs.exec_() + self.prefs.show() + + print(self.prefs._cfg.getBool(self.prefs._cfg.DEFAULT_IGNORE_RULES)) + + assert self.prefs.comboUIAction.currentIndex() == Config.ACTION_ALLOW_IDX and self.prefs.comboUIAction.currentText() == Config.ACTION_ALLOW + assert self.prefs.checkUIRules.isChecked() == True + assert self.prefs.comboUIRules.currentIndex() == 1 + assert self.prefs.comboUITarget.currentIndex() == 2 + assert self.prefs.comboUIDuration.currentIndex() == 4 and self.prefs.comboUIDuration.currentText() == Config.DURATION_30m + assert self.prefs.comboUIDialogPos.currentIndex() == 2 + assert self.prefs.spinUITimeout.value() == 30 diff --git a/ui/tests/dialogs/test_ruleseditor.py b/ui/tests/dialogs/test_ruleseditor.py new file mode 100644 index 0000000..2b06bd1 --- /dev/null +++ b/ui/tests/dialogs/test_ruleseditor.py @@ -0,0 +1,384 @@ +# +# pytest -v tests/dialogs/test_ruleseditor.py +# + +import json +from PyQt5 import QtCore, QtWidgets, QtGui + +from opensnitch.config import Config +from opensnitch.dialogs.ruleseditor import RulesEditorDialog + +class TestRulesEditor(): + + @classmethod + def setup_method(self): + white_icon = QtGui.QIcon("../res/icon-white.svg") + self.rd = RulesEditorDialog(appicon=white_icon) + self.rd.show() + self.rd.ruleNameEdit.setText("xxx") + self.rd.nodesCombo.addItem("unix:/tmp/osui.sock") + self.rd.nodesCombo.setCurrentText("unix:/tmp/osui.sock") + self.rd._nodes._nodes["unix:/tmp/osui.sock"] = {} + + def test_rule_no_fields(self, qtbot): + """ Test that rules without fields selected cannot be created. + """ + qtbot.addWidget(self.rd) + + def handle_dialog(): + qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Apply), QtCore.Qt.LeftButton) + qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton) + + QtCore.QTimer.singleShot(100, handle_dialog) + self.rd.exec_() + assert self.rd.statusLabel.text() != "" + + def test_fields_empty(self, qtbot): + """ Test that fields cannot be empty. + """ + + self.rd.pidCheck.setChecked(True) + self.rd.pidLine.setText("") + result, error = self.rd._save_rule() + assert error != None + + self.rd.pidCheck.setChecked(False) + self.rd.uidCheck.setChecked(True) + self.rd.uidLine.setText("") + result, error = self.rd._save_rule() + assert error != None + + self.rd.uidCheck.setChecked(False) + self.rd.procCheck.setChecked(True) + self.rd.procLine.setText("") + result, error = self.rd._save_rule() + assert error != None + + self.rd.procCheck.setChecked(False) + self.rd.cmdlineCheck.setChecked(True) + self.rd.cmdlineLine.setText("") + result, error = self.rd._save_rule() + assert error != None + + self.rd.cmdlineCheck.setChecked(False) + self.rd.dstPortCheck.setChecked(True) + self.rd.dstPortLine.setText("") + result, error = self.rd._save_rule() + assert error != None + + self.rd.dstPortCheck.setChecked(False) + self.rd.dstHostCheck.setChecked(True) + self.rd.dstHostLine.setText("") + result, error = self.rd._save_rule() + assert error != None + + self.rd.dstHostCheck.setChecked(False) + self.rd.dstListsCheck.setChecked(True) + self.rd.dstListsLine.setText("") + result, error = self.rd._save_rule() + assert error != None + + def test_add_basic_rule(self, qtbot): + """ Test adding a basic rule. + """ + + self.rd.statusLabel.setText("") + self.rd.ruleNameEdit.setText("www.test.com") + self.rd.dstHostCheck.setChecked(True) + self.rd.dstHostLine.setText("www.test.com") + self.rd.durationCombo.setCurrentIndex(self.rd._load_duration(Config.DURATION_UNTIL_RESTART)) + + def handle_dialog(): + qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Apply), QtCore.Qt.LeftButton) + qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton) + + QtCore.QTimer.singleShot(100, handle_dialog) + self.rd.exec_() + + assert self.rd.statusLabel.text() == "" + assert self.rd._db.get_rule("www.test.com", self.rd.nodesCombo.currentText()).next() == True + assert self.rd._old_rule_name == "www.test.com" + # after adding a rule, we enter into editing mode, to allow editing it + # without closing the dialog. + assert self.rd.WORK_MODE == self.rd.EDIT_RULE + assert self.rd.rule.operator.type == Config.RULE_TYPE_SIMPLE + assert self.rd.rule.operator.operand == "dest.host" + assert self.rd.rule.operator.data == "www.test.com" + assert self.rd.rule.duration == Config.DURATION_UNTIL_RESTART + + def test_add_complex_rule(self, qtbot): + """ Test add complex rule. + """ + self.rd.WORK_MODE = self.rd.ADD_RULE + self.rd._reset_state() + self.rd.statusLabel.setText("") + self.rd.ruleNameEdit.setText("www.test-complex.com") + self.rd.dstHostCheck.setChecked(True) + self.rd.dstHostLine.setText("www.test-complex.com") + self.rd.dstPortCheck.setChecked(True) + self.rd.dstPortLine.setText("443") + + def handle_dialog(): + qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Apply), QtCore.Qt.LeftButton) + qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton) + + QtCore.QTimer.singleShot(100, handle_dialog) + self.rd.exec_() + + assert self.rd.statusLabel.text() == "" + assert self.rd._db.get_rule("www.test-complex.com", self.rd.nodesCombo.currentText()).next() == True + assert self.rd._old_rule_name == "www.test-complex.com" + # after adding a rule, we enter into editing mode, to allow editing it + # without closing the dialog. + assert self.rd.WORK_MODE == self.rd.EDIT_RULE + assert self.rd.rule.operator.type == Config.RULE_TYPE_LIST + assert self.rd.rule.operator.operand == Config.RULE_TYPE_LIST + json_rule = json.loads(self.rd.rule.operator.data) + assert json_rule[0]['type'] == "simple" + assert json_rule[0]['operand'] == "dest.port" + assert json_rule[0]['data'] == "443" + assert json_rule[1]['type'] == "simple" + assert json_rule[1]['operand'] == "dest.host" + assert json_rule[1]['data'] == "www.test-complex.com" + + def test_add_reject_rule(self, qtbot): + """ Test adding new rule with action "reject". + """ + + self.rd.statusLabel.setText("") + self.rd.ruleNameEdit.setText("www.test-reject.com") + self.rd.dstHostCheck.setChecked(True) + self.rd.dstHostLine.setText("www.test-reject.com") + self.rd.actionRejectRadio.setChecked(True) + + def handle_dialog(): + qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Apply), QtCore.Qt.LeftButton) + qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton) + + QtCore.QTimer.singleShot(100, handle_dialog) + self.rd.exec_() + + assert self.rd.statusLabel.text() == "" + assert self.rd._db.get_rule("www.test-reject.com", self.rd.nodesCombo.currentText()).next() == True + assert self.rd._old_rule_name == "www.test-reject.com" + # after adding a rule, we enter into editing mode, to allow editing it + # without closing the dialog. + assert self.rd.WORK_MODE == self.rd.EDIT_RULE + assert self.rd.rule.operator.type == Config.RULE_TYPE_SIMPLE + assert self.rd.rule.operator.operand == "dest.host" + assert self.rd.rule.operator.data == "www.test-reject.com" + assert self.rd.rule.action == Config.ACTION_REJECT + + def test_add_deny_rule(self, qtbot): + """ Test adding new rule with action "deny". + """ + + self.rd.statusLabel.setText("") + self.rd.ruleNameEdit.setText("www.test-deny.com") + self.rd.dstHostCheck.setChecked(True) + self.rd.dstHostLine.setText("www.test-deny.com") + self.rd.actionDenyRadio.setChecked(True) + + def handle_dialog(): + qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Apply), QtCore.Qt.LeftButton) + qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton) + + QtCore.QTimer.singleShot(100, handle_dialog) + self.rd.exec_() + + assert self.rd.statusLabel.text() == "" + assert self.rd._db.get_rule("www.test-deny.com", self.rd.nodesCombo.currentText()).next() == True + assert self.rd._old_rule_name == "www.test-deny.com" + # after adding a rule, we enter into editing mode, to allow editing it + # without closing the dialog. + assert self.rd.WORK_MODE == self.rd.EDIT_RULE + assert self.rd.rule.operator.type == Config.RULE_TYPE_SIMPLE + assert self.rd.rule.operator.operand == "dest.host" + assert self.rd.rule.operator.data == "www.test-deny.com" + assert self.rd.rule.action == Config.ACTION_DENY + + def test_add_allow_rule(self, qtbot): + """ Test adding new rule with action "allow". + """ + + self.rd.statusLabel.setText("") + self.rd.ruleNameEdit.setText("www.test-allow.com") + self.rd.dstHostCheck.setChecked(True) + self.rd.dstHostLine.setText("www.test-allow.com") + self.rd.actionAllowRadio.setChecked(True) + + def handle_dialog(): + qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Apply), QtCore.Qt.LeftButton) + qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton) + + QtCore.QTimer.singleShot(100, handle_dialog) + self.rd.exec_() + + assert self.rd.statusLabel.text() == "" + assert self.rd._db.get_rule("www.test-allow.com", self.rd.nodesCombo.currentText()).next() == True + assert self.rd._old_rule_name == "www.test-allow.com" + # after adding a rule, we enter into editing mode, to allow editing it + # without closing the dialog. + assert self.rd.WORK_MODE == self.rd.EDIT_RULE + assert self.rd.rule.operator.type == Config.RULE_TYPE_SIMPLE + assert self.rd.rule.operator.operand == "dest.host" + assert self.rd.rule.operator.data == "www.test-allow.com" + assert self.rd.rule.action == Config.ACTION_ALLOW + + def test_add_rule_name_conflict(self, qtbot): + """ Test that rules with the same name cannot be added. + """ + assert self.rd._db.get_rule("www.test.com", self.rd.nodesCombo.currentText()).next() == True + + self.rd.statusLabel.setText("") + self.rd.ruleNameEdit.setText("www.test.com") + self.rd.dstHostCheck.setChecked(True) + self.rd.dstHostLine.setText("www.test.com") + + def handle_dialog(): + qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Apply), QtCore.Qt.LeftButton) + qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton) + + QtCore.QTimer.singleShot(100, handle_dialog) + self.rd.exec_() + + assert self.rd.statusLabel.text() != "" + + def test_load_rule(self, qtbot): + """ Test loading a rule. + """ + self.rd.WORK_MODE = self.rd.ADD_RULE + self.rd._reset_state() + records = self.rd._db.get_rule("www.test.com", self.rd.nodesCombo.currentText()) + assert records.next() == True + + self.rd.edit_rule(records, self.rd.nodesCombo.currentText()) + assert self.rd.WORK_MODE == self.rd.EDIT_RULE + assert self.rd.ruleNameEdit.text() == "www.test.com" + assert self.rd.dstHostCheck.isChecked() == True + assert self.rd.dstHostLine.text() == "www.test.com" + assert self.rd.durationCombo.currentIndex() == self.rd._load_duration(Config.DURATION_UNTIL_RESTART) + + def handle_dialog(): + qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton) + + QtCore.QTimer.singleShot(100, handle_dialog) + self.rd.exec_() + + def test_edit_and_rename_rule(self, qtbot): + """ Test loading, editing and renaming a rule. + """ + self.rd.WORK_MODE = self.rd.ADD_RULE + self.rd._reset_state() + records = self.rd._db.get_rule("www.test.com", self.rd.nodesCombo.currentText()) + assert records.next() == True + + self.rd.edit_rule(records, self.rd.nodesCombo.currentText()) + assert self.rd.WORK_MODE == self.rd.EDIT_RULE + assert self.rd.ruleNameEdit.text() == "www.test.com" + assert self.rd.dstHostCheck.isChecked() == True + assert self.rd.dstHostLine.text() == "www.test.com" + + self.rd.ruleNameEdit.setText("www.test-renamed.com") + self.rd.dstHostLine.setText("www.test-renamed.com") + + def handle_dialog(): + qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Apply), QtCore.Qt.LeftButton) + qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton) + + QtCore.QTimer.singleShot(100, handle_dialog) + self.rd.exec_() + + records = self.rd._db.get_rule("www.test.com", self.rd.nodesCombo.currentText()) + assert records.next() == False + records = self.rd._db.get_rule("www.test-renamed.com", self.rd.nodesCombo.currentText()) + assert records.next() == True + + def test_durations(self, qtbot): + """ Test adding new rule with action "deny". + """ + + self.rd.statusLabel.setText("") + self.rd.ruleNameEdit.setText("www.test-duration.com") + self.rd.dstHostCheck.setChecked(True) + self.rd.dstHostLine.setText("www.test-duration.com") + self.rd.actionDenyRadio.setChecked(True) + self.rd.durationCombo.setCurrentIndex(self.rd._load_duration(Config.DURATION_ALWAYS)) + + def handle_dialog(): + qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Apply), QtCore.Qt.LeftButton) + qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton) + + QtCore.QTimer.singleShot(100, handle_dialog) + self.rd.exec_() + + assert self.rd.statusLabel.text() == "" + assert self.rd._db.get_rule("www.test-duration.com", self.rd.nodesCombo.currentText()).next() == True + assert self.rd._old_rule_name == "www.test-duration.com" + # after adding a rule, we enter into editing mode, to allow editing it + # without closing the dialog. + assert self.rd.WORK_MODE == self.rd.EDIT_RULE + assert self.rd.rule.operator.type == Config.RULE_TYPE_SIMPLE + assert self.rd.rule.operator.operand == "dest.host" + assert self.rd.rule.operator.data == "www.test-duration.com" + assert self.rd.rule.action == Config.ACTION_DENY + assert self.rd.rule.duration == Config.DURATION_ALWAYS + + def test_rule_LANs(self, qtbot): + """ Test rule with regexp and LAN keyword in particular. + """ + self.rd.statusLabel.setText("") + self.rd.ruleNameEdit.setText("www.test-rule-LAN.com") + self.rd.dstIPCheck.setChecked(True) + self.rd.dstIPCombo.setCurrentText(self.rd.LAN_LABEL) + self.rd.actionDenyRadio.setChecked(True) + self.rd.durationCombo.setCurrentIndex(self.rd._load_duration(Config.DURATION_ALWAYS)) + + def handle_dialog(): + qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Apply), QtCore.Qt.LeftButton) + qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton) + + QtCore.QTimer.singleShot(100, handle_dialog) + self.rd.exec_() + + assert self.rd.statusLabel.text() == "" + assert self.rd._db.get_rule("www.test-rule-LAN.com", self.rd.nodesCombo.currentText()).next() == True + assert self.rd._old_rule_name == "www.test-rule-LAN.com" + # after adding a rule, we enter into editing mode, to allow editing it + # without closing the dialog. + assert self.rd.WORK_MODE == self.rd.EDIT_RULE + assert self.rd.rule.operator.type == Config.RULE_TYPE_REGEXP + assert self.rd.rule.operator.operand == "dest.ip" + assert self.rd.rule.operator.data == self.rd.LAN_RANGES + assert self.rd.rule.action == Config.ACTION_DENY + assert self.rd.rule.duration == Config.DURATION_ALWAYS + + def test_rule_networks(self, qtbot): + """ Test rule with networks. + """ + self.rd.statusLabel.setText("") + self.rd.ruleNameEdit.setText("www.test-rule-networks.com") + self.rd.dstIPCheck.setChecked(True) + self.rd.dstIPCombo.setCurrentText("192.168.111.0/24") + self.rd.actionDenyRadio.setChecked(True) + self.rd.durationCombo.setCurrentIndex(self.rd._load_duration(Config.DURATION_ALWAYS)) + + def handle_dialog(): + qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Apply), QtCore.Qt.LeftButton) + qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton) + + QtCore.QTimer.singleShot(100, handle_dialog) + self.rd.exec_() + + assert self.rd.statusLabel.text() == "" + assert self.rd._db.get_rule("www.test-rule-networks.com", self.rd.nodesCombo.currentText()).next() == True + assert self.rd._old_rule_name == "www.test-rule-networks.com" + # after adding a rule, we enter into editing mode, to allow editing it + # without closing the dialog. + assert self.rd.WORK_MODE == self.rd.EDIT_RULE + assert self.rd.rule.operator.type == Config.RULE_TYPE_NETWORK + assert self.rd.rule.operator.operand == "dest.network" + assert self.rd.rule.operator.data == "192.168.111.0/24" + assert self.rd.rule.action == Config.ACTION_DENY + assert self.rd.rule.duration == Config.DURATION_ALWAYS + diff --git a/ui/tests/test_nodes.py b/ui/tests/test_nodes.py new file mode 100644 index 0000000..d4c4ae5 --- /dev/null +++ b/ui/tests/test_nodes.py @@ -0,0 +1,149 @@ +# +# pytest -v tests/nodes.py +# + +import json +from PyQt5 import QtCore +from opensnitch import ui_pb2 +from opensnitch.config import Config +from opensnitch.nodes import Nodes +from tests.dialogs import ClientConfig + +class NotifTest(QtCore.QObject): + """We need to subclass from QObject in order to be able to user signals and slots. + """ + signal = QtCore.pyqtSignal(ui_pb2.NotificationReply) + + @QtCore.pyqtSlot(ui_pb2.NotificationReply) + def callback(self, reply): + assert reply != None + assert reply.code == ui_pb2.OK and reply.type == ui_pb2.LOAD_FIREWALL and reply.data == "test" + + +class TestNodes(): + + @classmethod + def setup_method(self): + self.nid = None + self.daemon_config = ClientConfig + self.nodes = Nodes.instance() + self.nodes._db.insert("nodes", + "(addr, status, hostname, daemon_version, daemon_uptime, " \ + "daemon_rules, cons, cons_dropped, version, last_connection)", + ( + "1.2.3.4", Nodes.ONLINE, "xxx", "v1.2.3", str(0), + "", "0", "0", "", + "2022-01-03 11:22:48.101624" + ) + ) + + + def test_add(self, qtbot): + node = self.nodes.add("peer:1.2.3.4", self.daemon_config) + + assert node != None + + def test_get_node(self, qtbot): + node = self.nodes.get_node("peer:1.2.3.4") + + assert node != None + + def test_get_addr(self, qtbot): + proto, addr = self.nodes.get_addr("peer:1.2.3.4") + + assert proto == "peer" and addr == "1.2.3.4" + + def test_get_nodes(self, qtbot): + nodes = self.nodes.get_nodes() + print(nodes) + + assert nodes.get("peer:1.2.3.4") != None + + def test_add_rule(self, qtbot): + self.nodes.add_rule( + "2022-01-03 11:22:48.101624", + "peer:1.2.3.4", + "test", + True, + False, + Config.ACTION_ALLOW, Config.DURATION_30s, + Config.RULE_TYPE_SIMPLE, False, "dest.host", "" + ) + + query = self.nodes._db.get_rule("test", "peer:1.2.3.4") + + assert query.first() == True + assert query.record().value(0) == "2022-01-03 11:22:48.101624" + assert query.record().value(1) == "peer:1.2.3.4" + assert query.record().value(2) == "test" + assert query.record().value(3) == "1" + assert query.record().value(4) == "0" + assert query.record().value(5) == Config.ACTION_ALLOW + assert query.record().value(6) == Config.DURATION_30s + assert query.record().value(7) == Config.RULE_TYPE_SIMPLE + assert query.record().value(8) == "0" + assert query.record().value(9) == "dest.host" + + def test_update_rule_time(self, qtbot): + query = self.nodes._db.get_rule("test", "peer:1.2.3.4") + assert query.first() == True + assert query.record().value(0) == "2022-01-03 11:22:48.101624" + + self.nodes.update_rule_time("2022-01-03 21:22:48.101624", "test", "peer:1.2.3.4") + query = self.nodes._db.get_rule("test", "peer:1.2.3.4") + assert query.first() == True + assert query.record().value(0) == "2022-01-03 21:22:48.101624" + + def test_delete_rule(self, qtbot): + query = self.nodes._db.get_rule("test", "peer:1.2.3.4") + assert query.first() == True + + self.nodes.delete_rule("test", "peer:1.2.3.4", None) + query = self.nodes._db.get_rule("test", "peer:1.2.3.4") + assert query.first() == False + + def test_update_node_status(self, qtbot): + query = self.nodes._db.select("SELECT status FROM nodes WHERE addr = '{0}'".format("1.2.3.4")) + assert query != None and query.exec_() == True and query.first() == True + assert query.record().value(0) == Nodes.ONLINE + + self.nodes.update("peer", "1.2.3.4", Nodes.OFFLINE) + query = self.nodes._db.select("SELECT status FROM nodes WHERE addr = '{0}'".format("1.2.3.4")) + assert query != None and query.exec_() == True and query.first() == True + assert query.record().value(0) == Nodes.OFFLINE + + def test_send_notification(self, qtbot): + notifs = NotifTest() + notifs.signal.connect(notifs.callback) + + test_notif = ui_pb2.Notification( + clientName="", + serverName="", + type=ui_pb2.LOAD_FIREWALL, + data="test", + rules=[]) + + self.nid = self.nodes.send_notification("peer:1.2.3.4", test_notif, notifs.signal) + assert self.nodes._notifications_sent[self.nid] != None + assert self.nodes._notifications_sent[self.nid]['type'] == ui_pb2.LOAD_FIREWALL + + def test_reply_notification(self, qtbot): + reply_notif = ui_pb2.Notification( + id = self.nid, + clientName="", + serverName="", + type=ui_pb2.LOAD_FIREWALL, + data="test", + rules=[]) + # just after process the reply, the notification is deleted (except if + # is of type MONITOR_PROCESS + self.nodes.reply_notification("peer:1.2.3.4", reply_notif) + assert self.nid not in self.nodes._notifications_sent + + def test_delete(self, qtbot): + self.nodes.delete("peer:1.2.3.4") + node = self.nodes.get_node("peer:1.2.3.4") + nodes = self.nodes.get_nodes() + + assert node == None + assert nodes.get("peer:1.2.3.4") == None diff --git a/utils/legacy/make_ads_rules.py b/utils/legacy/make_ads_rules.py new file mode 100644 index 0000000..16341c5 --- /dev/null +++ b/utils/legacy/make_ads_rules.py @@ -0,0 +1,69 @@ +import requests +import re +import ipaddress +import datetime +import os + +lists = ( \ + "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts", + "https://mirror1.malwaredomains.com/files/justdomains", + "https://sysctl.org/cameleon/hosts", + "https://zeustracker.abuse.ch/blocklist.php?download=domainblocklist", + "https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt", + "https://s3.amazonaws.com/lists.disconnect.me/simple_ad.txt", + "https://hosts-file.net/ad_servers.txt" ) + +domains = {} + +for url in lists: + print "Downloading %s ..." % url + r = requests.get(url) + if r.status_code != 200: + print "Error, status code %d" % r.status_code + continue + + for line in r.text.split("\n"): + line = line.strip() + if line == "": + continue + + elif line[0] == "#": + continue + + for part in re.split(r'\s+', line): + part = part.strip() + if part == "": + continue + + try: + duh = ipaddress.ip_address(part) + except ValueError: + if part != "localhost": + domains[part] = 1 + +print "Got %d unique domains, saving as rules to ./rules/ ..." % len(domains) + +os.system("mkdir -p rules") + +idx = 0 +for domain, _ in domains.iteritems(): + with open("rules/adv-%d.json" % idx, "wt") as fp: + tpl = """ +{ + "created": "%s", + "updated": "%s", + "name": "deny-adv-%d", + "enabled": true, + "action": "deny", + "duration": "always", + "operator": { + "type": "simple", + "operand": "dest.host", + "data": "%s" + } +}""" + now = datetime.datetime.utcnow().isoformat("T") + "Z" + data = tpl % ( now, now, idx, domain ) + fp.write(data) + + idx = idx + 1 diff --git a/utils/packaging/build_modules.sh b/utils/packaging/build_modules.sh new file mode 100644 index 0000000..51abc7b --- /dev/null +++ b/utils/packaging/build_modules.sh @@ -0,0 +1,76 @@ +#!/bin/sh +# +# opensnitch - 2022-2023 +# +echo """ + + Dependencies needed to compile the eBPF modules: + sudo apt install -y wget flex bison ca-certificates wget python3 rsync bc libssl-dev clang llvm libelf-dev libzip-dev git libpcap-dev + --- +""" + +kernel_version=$(uname -r | cut -d. -f1,2) +if [ ! -z $1 ]; then + kernel_version=$1 +fi + +kernel_sources="v${kernel_version}.tar.gz" + +if [ -f "${kernel_sources}" ]; then + echo -n "[i] Deleting previous kernel sources ${kernel_sources}: " + rm -f ${kernel_sources} && echo "OK" || echo "ERROR" +fi +echo "[+] Downloading kernel sources:" +wget -nv --show-progress https://github.com/torvalds/linux/archive/${kernel_sources} 1>/dev/null +echo + +if [ -d "linux-${kernel_version}/" ]; then + echo -n "[i] Deleting previous kernel sources dir linux-${kernel_version}/: " + rm -rf linux-${kernel_version}/ && echo "OK" || echo "ERROR" +fi +echo -n "[+] Uncompressing kernel sources: " +tar -xf v${kernel_version}.tar.gz && echo "OK" || echo "ERROR" + +if [ "${ARCH}" == "arm" -o "${ARCH}" == "arm64" ]; then + echo "[+] Patching kernel sources" + patch linux-${kernel_version}/arch/arm/include/asm/unified.h < ebpf_prog/arm-clang-asm-fix.patch +fi + +echo -n "[+] Preparing kernel sources... (1-2 minutes): " +echo -n "." +cd linux-${kernel_version} && yes "" | make oldconfig 1>/dev/null +echo -n "." +make prepare 1>/dev/null +echo -n "." +make headers_install 1>/dev/null +echo " DONE" +cd ../ + +if [ -z $ARCH ]; then + ARCH=x86 +fi + +echo "[+] Compiling eBPF modules..." +cd ebpf_prog && make KERNEL_DIR=../linux-${kernel_version} KERNEL_HEADERS=../linux-${kernel_version} ARCH=${ARCH} >/dev/null +# objdump -h opensnitch.o #you should see many section, number 1 should be called kprobe/tcp_v4_connect + +if [ ! -d modules/ ]; then + mkdir modules/ +fi +mv opensnitch*o modules/ +cd ../ +llvm-strip -g ebpf_prog/modules/opensnitch*.o #remove debug info + +if [ -f ebpf_prog/modules/opensnitch.o ]; then + echo + if objdump -h ebpf_prog/modules/opensnitch.o | grep "kprobe/tcp_v4_connect"; then + ls ebpf_prog/modules/*.o + echo -e "\n * eBPF modules compiled. Now you can copy the *.o files to /etc/opensnitchd/ and restart the daemon\n" + else + echo -e "\n [WARN] opensnitch.o module not valid\n" + exit 1 + fi +else + echo -e "\n [WARN] opensnitch.o module not compiled\n" + exit 1 +fi diff --git a/utils/packaging/daemon/deb/debian/NEWS b/utils/packaging/daemon/deb/debian/NEWS new file mode 100644 index 0000000..837c995 --- /dev/null +++ b/utils/packaging/daemon/deb/debian/NEWS @@ -0,0 +1,20 @@ +opensnitch (1.6.0-rc.3-1) unstable; urgency=medium + + From now on the eBPF modules will be installed under + /usr/lib/opensnitchd/ebpf/. + + The daemon will look for the eBPF modules in these directories and order: + - /usr/local/lib/opensnitchd/ebpf/ + - /usr/lib/opensnitchd/ebpf/ + + Modules under /etc/opensnitchd/ will still be loaded if found, but it's + deprecated and will be removed in the future. + + There's a new module to intercept processes execution. It may cause some + rules not to match: for example if you allowed /bin/telnet, now it may be + reported as /usr/bin/inteutils-telnet + + These cases are mostly expected. We'll keep improving it, sorry for + the inconveniences. + + -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Wed, 19 Oct 2022 00:15:19 +0200 diff --git a/utils/packaging/daemon/deb/debian/changelog b/utils/packaging/daemon/deb/debian/changelog new file mode 100644 index 0000000..3a4c641 --- /dev/null +++ b/utils/packaging/daemon/deb/debian/changelog @@ -0,0 +1,247 @@ +opensnitch (1.6.9-1) unstable; urgency=medium + + * Non-maintainer upload. + + -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Mon, 28 Apr 2025 01:31:50 +0200 + +opensnitch (1.6.6-1) unstable; urgency=medium + + * Non-maintainer upload. + + -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Thu, 20 Jun 2024 00:02:56 +0200 + +opensnitch (1.6.5-1) unstable; urgency=medium + + * Non-maintainer upload. + + -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Tue, 23 Jan 2024 21:25:33 +0100 + +opensnitch (1.6.2-1) unstable; urgency=medium + + * Non-maintainer upload. + + -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Mon, 31 Jul 2023 00:32:11 +0200 + +opensnitch (1.6.1-1) unstable; urgency=medium + + * Non-maintainer upload. + + -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Sun, 23 Jul 2023 22:13:33 +0200 + +opensnitch (1.6.0.1-1) unstable; urgency=medium + + * Non-maintainer upload. + + -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Thu, 15 Jun 2023 23:44:15 +0200 + +opensnitch (1.6.0-rc.5-1) unstable; urgency=medium + + * Non-maintainer upload. + + -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Sat, 18 Feb 2023 20:07:14 +0100 + +opensnitch (1.6.0-rc.4-1) unstable; urgency=medium + + * Non-maintainer upload. + + -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Thu, 22 Dec 2022 10:07:14 +0100 + +opensnitch (1.6.0-rc.3-1) unstable; urgency=medium + + * eBPF modules are now installed under /usr/lib/opensnitchd/. + + -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Thu, 08 Dec 2022 11:13:09 +0100 + +opensnitch (1.6.0-rc.2-1) unstable; urgency=medium + + * New eBPF module to receive events (new execs, etc). + * Allow to exclude connections from the events. + * more on github + + -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Thu, 14 Jul 2022 00:15:19 +0200 + +opensnitch (1.6.0-rc.1-1) unstable; urgency=medium + + * Allow to configure firewall from the GUI. + * New eBPF module to intercept outbound DNS requests. + + -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Wed, 04 May 2022 00:55:54 +0200 + +opensnitch (1.5.0-1) unstable; urgency=medium + + * New release. + * Added Reject option. + * New lists types to block ads/malware/... + * Better connections interception. + * Better VPNs handling. + * Bug fixes. + + -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Fri, 28 Jan 2022 23:20:38 +0100 + +opensnitch (1.5.0~rc2-1) unstable; urgency=medium + + * Better connections interception. + * Improvements. + + -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Sun, 16 Jan 2022 23:15:12 +0100 + +opensnitch (1.5.0~rc1-1) unstable; urgency=medium + + * New features. + + -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Thu, 07 Oct 2021 14:57:35 +0200 + +opensnitch (1.4.0-1) unstable; urgency=medium + + * final release. + + -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Fri, 27 Aug 2021 13:33:07 +0200 + +opensnitch (1.4.0~rc4-1) unstable; urgency=medium + + * Bug fix release. + + -- gustavo-iniguez-goya <gooffy1@gmail.com> Wed, 11 Aug 2021 15:17:49 +0200 + +opensnitch (1.4.0~rc3-1) unstable; urgency=medium + + * Bug fix release. + + -- gustavo-iniguez-goya <gooffy1@gmail.com> Fri, 16 Jul 2021 23:28:52 +0200 + +opensnitch (1.4.0~rc2-1) unstable; urgency=medium + + * Added eBPF support. + * Fixes and improvements. + + -- gustavo-iniguez-goya <gooffy1@gmail.com> Fri, 07 May 2021 01:08:02 +0200 + +opensnitch (1.4.0~rc-1) unstable; urgency=medium + + * Bug fix and improvements release. + + -- gustavo-iniguez-goya <gooffy1@gmail.com> Thu, 25 Mar 2021 01:02:31 +0100 + +opensnitch (1.3.6-1) unstable; urgency=medium + + * Bug fix and improvements release. + + -- gustavo-iniguez-goya <gooffy1@gmail.com> Wed, 10 Feb 2021 10:17:43 +0100 + +opensnitch (1.3.5-1) unstable; urgency=medium + + * Bug fix and improvements release. + + -- gustavo-iniguez-goya <gooffy1@gmail.com> Mon, 11 Jan 2021 18:01:53 +0100 + +opensnitch (1.3.0-1) unstable; urgency=medium + + * Fixed how we check rules + * Fixed cpu spike after disable interception. + * Fixed cleaning up fw rules on exit. + * make regexp rules case-insensitive by default + * allow to filter by dst network. + + -- gustavo-iniguez-goya <gooffy1@gmail.com> Wed, 16 Dec 2020 01:15:03 +0100 + +opensnitch (1.3.0~rc-1) unstable; urgency=medium + + * Non-maintainer upload. + + -- gustavo-iniguez-goya <gooffy1@gmail.com> Fri, 13 Nov 2020 00:51:34 +0100 + +opensnitch (1.2.0-1) unstable; urgency=medium + + * Fixed memleaks. + * Sort rules by name + * Added priority field to rules. + * Other fixes + + -- gustavo-iniguez-goya <gooffy1@gmail.com> Mon, 09 Nov 2020 22:55:13 +0100 + +opensnitch (1.0.1-1) unstable; urgency=medium + + * Fixed app exit when IPv6 is not supported. + * Other fixes. + + -- gustavo-iniguez-goya <gooffy1@gmail.com> Thu, 30 Jul 2020 21:56:20 +0200 + +opensnitch (1.0.0-1) unstable; urgency=medium + + * v1.0.0 released. + + -- gustavo-iniguez-goya <gooffy1@gmail.com> Thu, 16 Jul 2020 00:19:26 +0200 + +opensnitch (1.0.0rc11-1) unstable; urgency=medium + + * Fixed multiple race conditions. + * Fixed CWD parsing when using audit proc monitor method. + + -- gustavo-iniguez-goya <gooffy1@gmail.com> Wed, 24 Jun 2020 00:10:38 +0200 + +opensnitch (1.0.0rc10-1) unstable; urgency=medium + + * Fixed checking UID functions availability. + * Improved process path parsing. + * Fixed applying config from the UI. + * Fixed default log level. + * Gather CWD and process environment vars. + * Increase default timeout when asking for a rule. + + -- gustavo-iniguez-goya <gooffy1@gmail.com> Sat, 13 Jun 2020 18:45:02 +0200 + +opensnitch (1.0.0rc9-1) unstable; urgency=medium + + * Ignore malformed rules from loading. + * Allow to modify and add rules from the UI. + + -- gustavo-iniguez-goya <gooffy1@gmail.com> Sun, 17 May 2020 18:18:24 +0200 + +opensnitch (1.0.0rc8) unstable; urgency=medium + + * Allow to change settings from the UI. + * Improved connection handling with the UI. + + -- gustavo-iniguez-goya <gooffy1@gmail.com> Wed, 29 Apr 2020 21:52:27 +0200 + +opensnitch (1.0.0rc7-1) unstable; urgency=medium + + * Stability, performance and realiability improvements. + + -- gustavo-iniguez-goya <gooffy1@gmail.com> Sun, 12 Apr 2020 23:25:41 +0200 + +opensnitch (1.0.0rc6-1) unstable; urgency=medium + + * Fixed iptables rules deletion. + * Improved PIDs cache. + * Added audit process monitoring method. + * Added logrotate file. + * Added default configuration file. + + -- gustavo-iniguez-goya <gooffy1@gmail.com> Sun, 08 Mar 2020 20:47:58 +0100 + +opensnitch (1.0.0rc-5) unstable; urgency=medium + + * Fixed netlink socket querying. + * Added check to reload firewall rules if missing. + + -- gustavo-iniguez-goya <gooffy1@gmail.com> Mon, 24 Feb 2020 19:55:06 +0100 + +opensnitch (1.0.0rc-3) unstable; urgency=medium + + * @see: https://github.com/gustavo-iniguez-goya/opensnitch/releases + + -- gustavo-iniguez-goya <gooffy1@gmail.com> Tue, 18 Feb 2020 10:09:45 +0100 + +opensnitch (1.0.0rc-2) unstable; urgency=medium + + * UI minor changes + * Expand deb package compatibility. + + -- gustavo-iniguez-goya <gooffy1@gmail.com> Wed, 05 Feb 2020 21:50:20 +0100 + +opensnitch (1.0.0rc-1) unstable; urgency=medium + + * Initial release + + -- gustavo-iniguez-goya <gooffy1@gmail.com> Fri, 22 Nov 2019 01:14:08 +0100 diff --git a/utils/packaging/daemon/deb/debian/control b/utils/packaging/daemon/deb/debian/control new file mode 100644 index 0000000..c3ffc8b --- /dev/null +++ b/utils/packaging/daemon/deb/debian/control @@ -0,0 +1,49 @@ +Source: opensnitch +Maintainer: Debian Go Packaging Team <team+pkg-go@tracker.debian.org> +Uploaders: Gustavo Iniguez Goya <gooffy@gmail.com> +Section: devel +Testsuite: autopkgtest-pkg-go +Priority: optional +Build-Depends: + debhelper-compat (= 11), + debhelper (>= 9), + dh-golang, + golang-any, + golang-github-vishvananda-netlink-dev, + golang-github-google-gopacket-dev, + golang-github-fsnotify-fsnotify-dev, + golang-golang-x-net-dev, + golang-google-grpc-dev, + golang-goprotobuf-dev, + pkg-config, + libnetfilter-queue-dev, + libmnl-dev +Standards-Version: 4.4.0 +Vcs-Browser: https://salsa.debian.org/go-team/packages/opensnitch +Vcs-Git: https://salsa.debian.org/go-team/packages/opensnitch.git +Homepage: https://github.com/evilsocket/opensnitch +Rules-Requires-Root: no +XS-Go-Import-Path: github.com/evilsocket/opensnitch + +Package: opensnitch +Section: net +Architecture: any +Depends: + libnetfilter-queue1, libc6, libnfnetlink0 +Built-Using: ${misc:Built-Using} +Description: GNU/Linux interactive application firewall + OpenSnitch is a GNU/Linux firewall application. + Whenever a program makes a connection, it'll prompt the user to allow or deny + it. + . + The user can decide if block the outgoing connection based on properties of + the connection: by port, by uid, by dst ip, by program or a combination + of them. + . + These rules can last forever, until the app restart or just one time. + . + The GUI allows the user to view live outgoing connections, as well as search + by process, user, host or port. + . + OpenSnitch can also work as a system-wide domains blocker, by using lists + of domains, list of IPs or list of regular expressions. diff --git a/utils/packaging/daemon/deb/debian/copyright b/utils/packaging/daemon/deb/debian/copyright new file mode 100644 index 0000000..ee4ba23 --- /dev/null +++ b/utils/packaging/daemon/deb/debian/copyright @@ -0,0 +1,31 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Source: https://github.com/evilsocket/opensnitch +Upstream-Name: opensnitch +Files-Excluded: + Godeps/_workspace + +Files: * +Copyright: + 2017-2018 evilsocket + 2019-2020 Gustavo Iñiguez Goia +Comment: Debian packaging is licensed under the same terms as upstream +License: GPL-3.0 + This program is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later + version. + . + This program is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the GNU General Public License for more + details. + . + You should have received a copy of the GNU General Public + License along with this program. If not, If not, see + http://www.gnu.org/licenses/. + . + On Debian systems, the full text of the GNU General Public + License version 3 can be found in the file + '/usr/share/common-licenses/GPL-3'. diff --git a/utils/packaging/daemon/deb/debian/gbp.conf b/utils/packaging/daemon/deb/debian/gbp.conf new file mode 100644 index 0000000..cec628c --- /dev/null +++ b/utils/packaging/daemon/deb/debian/gbp.conf @@ -0,0 +1,2 @@ +[DEFAULT] +pristine-tar = True diff --git a/utils/packaging/daemon/deb/debian/gitlab-ci.yml b/utils/packaging/daemon/deb/debian/gitlab-ci.yml new file mode 100644 index 0000000..91ff7ea --- /dev/null +++ b/utils/packaging/daemon/deb/debian/gitlab-ci.yml @@ -0,0 +1,27 @@ +# auto-generated, DO NOT MODIFY. +# The authoritative copy of this file lives at: +# https://salsa.debian.org/go-team/ci/blob/master/config/gitlabciyml.go + +# TODO: publish under debian-go-team/ci +image: stapelberg/ci2 + +test_the_archive: + artifacts: + paths: + - before-applying-commit.json + - after-applying-commit.json + script: + # Create an overlay to discard writes to /srv/gopath/src after the build: + - "rm -rf /cache/overlay/{upper,work}" + - "mkdir -p /cache/overlay/{upper,work}" + - "mount -t overlay overlay -o lowerdir=/srv/gopath/src,upperdir=/cache/overlay/upper,workdir=/cache/overlay/work /srv/gopath/src" + - "export GOPATH=/srv/gopath" + - "export GOCACHE=/cache/go" + # Build the world as-is: + - "ci-build -exemptions=/var/lib/ci-build/exemptions.json > before-applying-commit.json" + # Copy this package into the overlay: + - "GBP_CONF_FILES=:debian/gbp.conf gbp buildpackage --git-no-pristine-tar --git-ignore-branch --git-ignore-new --git-export-dir=/tmp/export --git-no-overlay --git-tarball-dir=/nonexistant --git-cleaner=/bin/true --git-builder='dpkg-buildpackage -S -d --no-sign'" + - "pgt-gopath -dsc /tmp/export/*.dsc" + # Rebuild the world: + - "ci-build -exemptions=/var/lib/ci-build/exemptions.json > after-applying-commit.json" + - "ci-diff before-applying-commit.json after-applying-commit.json" diff --git a/utils/packaging/daemon/deb/debian/opensnitch.init b/utils/packaging/daemon/deb/debian/opensnitch.init new file mode 100644 index 0000000..77ce353 --- /dev/null +++ b/utils/packaging/daemon/deb/debian/opensnitch.init @@ -0,0 +1,78 @@ +#!/bin/sh + +### BEGIN INIT INFO +# Provides: opensnitchd +# Required-Start: $network $local_fs +# Required-Stop: $network $local_fs +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: opensnitchd daemon +# Description: opensnitch application firewall +### END INIT INFO + +NAME=opensnitchd +PIDDIR=/var/run/$NAME +OPENSNITCHDPID=$PIDDIR/$NAME.pid + +# clear conflicting settings from the environment +unset TMPDIR + +test -x /usr/bin/$NAME || exit 0 + +. /lib/lsb/init-functions + +case $1 in + start) + log_daemon_msg "Starting opensnitch daemon" $NAME + if [ ! -d /etc/$NAME/rules ]; then + mkdir -p /etc/$NAME/rules &>/dev/null + fi + + # Make sure we have our PIDDIR, even if it's on a tmpfs + install -o root -g root -m 755 -d $PIDDIR + + if ! start-stop-daemon --start --quiet --oknodo --pidfile $OPENSNITCHDPID --background --exec /usr/bin/$NAME -- -rules-path /etc/$NAME/rules; then + log_end_msg 1 + exit 1 + fi + + log_end_msg 0 + ;; + stop) + + log_daemon_msg "Stopping $NAME daemon" $NAME + + start-stop-daemon --stop --quiet --signal QUIT --name $NAME + # Wait a little and remove stale PID file + sleep 1 + if [ -f $OPENSNITCHDPID ] && ! ps h `cat $OPENSNITCHDPID` > /dev/null + then + rm -f $OPENSNITCHDPID + fi + + log_end_msg 0 + + ;; + reload) + log_daemon_msg "Reloading $NAME" $NAME + + start-stop-daemon --stop --quiet --signal HUP --pidfile $OPENSNITCHDPID + + log_end_msg 0 + ;; + restart|force-reload) + $0 stop + sleep 1 + $0 start + ;; + status) + status_of_proc /usr/bin/$NAME $NAME + exit $? + ;; + *) + echo "Usage: /etc/init.d/opensnitchd {start|stop|reload|restart|force-reload|status}" + exit 1 + ;; +esac + +exit 0 diff --git a/utils/packaging/daemon/deb/debian/opensnitch.install b/utils/packaging/daemon/deb/debian/opensnitch.install new file mode 100644 index 0000000..6c635c4 --- /dev/null +++ b/utils/packaging/daemon/deb/debian/opensnitch.install @@ -0,0 +1,5 @@ +daemon/default-config.json etc/opensnitchd/ +daemon/system-fw.json etc/opensnitchd/ +ebpf_prog/opensnitch.o usr/lib/opensnitchd/ebpf/ +ebpf_prog/opensnitch-dns.o usr/lib/opensnitchd/ebpf/ +ebpf_prog/opensnitch-procs.o usr/lib/opensnitchd/ebpf/ diff --git a/utils/packaging/daemon/deb/debian/opensnitch.logrotate b/utils/packaging/daemon/deb/debian/opensnitch.logrotate new file mode 100644 index 0000000..7e1d486 --- /dev/null +++ b/utils/packaging/daemon/deb/debian/opensnitch.logrotate @@ -0,0 +1,13 @@ +/var/log/opensnitchd.log { + rotate 7 +# order of the fields is important + maxsize 50M +# we need this option in order to keep logging + copytruncate + missingok + notifempty + delaycompress + compress + create 640 root root + weekly +} diff --git a/utils/packaging/daemon/deb/debian/opensnitch.service b/utils/packaging/daemon/deb/debian/opensnitch.service new file mode 100644 index 0000000..b4301a5 --- /dev/null +++ b/utils/packaging/daemon/deb/debian/opensnitch.service @@ -0,0 +1,17 @@ +[Unit] +Description=Application firewall OpenSnitch +Documentation=https://github.com/gustavo-iniguez-goya/opensnitch/wiki +Wants=network.target +After=network.target + +[Service] +Type=simple +PermissionsStartOnly=true +ExecStartPre=/bin/mkdir -p /etc/opensnitchd/rules +ExecStart=/usr/bin/opensnitchd -rules-path /etc/opensnitchd/rules +Restart=always +RestartSec=30 +TimeoutStopSec=10 + +[Install] +WantedBy=multi-user.target diff --git a/utils/packaging/daemon/deb/debian/rules b/utils/packaging/daemon/deb/debian/rules new file mode 100755 index 0000000..d7cacbb --- /dev/null +++ b/utils/packaging/daemon/deb/debian/rules @@ -0,0 +1,19 @@ +#!/usr/bin/make -f +export DH_VERBOSE = 1 +export DESTDIR = debian/opensnitch + +override_dh_installsystemd: + dh_installsystemd --restart-after-upgrade + +execute_before_dh_auto_build: + cd proto; make ../daemon/ui/protocol/ui.pb.go + +execute_before_dh_auto_install: + mkdir -p $(DESTDIR)/usr/bin + mv _build/bin/daemon $(DESTDIR)/usr/bin/opensnitchd + +override_dh_auto_install: + dh_auto_install -- --no-source + +%: + dh $@ --builddirectory=_build --buildsystem=golang --with=golang diff --git a/utils/packaging/daemon/deb/debian/source/format b/utils/packaging/daemon/deb/debian/source/format new file mode 100644 index 0000000..163aaf8 --- /dev/null +++ b/utils/packaging/daemon/deb/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/utils/packaging/daemon/deb/debian/watch b/utils/packaging/daemon/deb/debian/watch new file mode 100644 index 0000000..383dd73 --- /dev/null +++ b/utils/packaging/daemon/deb/debian/watch @@ -0,0 +1,4 @@ +version=4 +opts=filenamemangle=s/.+\/v?(\d\S*)\.tar\.gz/opensnitch-\$1\.tar\.gz/,\ +uversionmangle=s/(\d)[_\.\-\+]?(RC|rc|pre|dev|beta|alpha)[.]?(\d*)$/\$1~\$2\$3/ \ + https://github.com/evilsocket/opensnitch/tags .*/v?(\d\S*)\.tar\.gz diff --git a/utils/packaging/daemon/rpm/opensnitch.spec b/utils/packaging/daemon/rpm/opensnitch.spec new file mode 100644 index 0000000..2497493 --- /dev/null +++ b/utils/packaging/daemon/rpm/opensnitch.spec @@ -0,0 +1,100 @@ +Name: opensnitch +Version: 1.6.9 +Release: 1%{?dist} +Summary: OpenSnitch is a GNU/Linux interactive application firewall + +License: GPLv3+ +URL: https://github.com/evilsocket/%{name} +Source0: https://github.com/evilsocket/%{name}/releases/download/v%{version}/%{name}_%{version}.orig.tar.gz +#BuildArch: x86_64 + +#BuildRequires: godep +Requires(post): info +Requires(preun): info + +%description +Whenever a program makes a connection, it'll prompt the user to allow or deny +it. + +The user can decide if block the outgoing connection based on properties of +the connection: by port, by uid, by dst ip, by program or a combination +of them. + +These rules can last forever, until the app restart or just one time. + +The GUI allows the user to view live outgoing connections, as well as search +by process, user, host or port. + +%prep +rm -rf %{buildroot} + +%setup + +%build +mkdir -p go/src/github.com/evilsocket +ln -s $(pwd) go/src/github.com/evilsocket/opensnitch +export GOPATH=$(pwd)/go +cd go/src/github.com/evilsocket/opensnitch/ +make protocol +cd go/src/github.com/evilsocket/opensnitch/daemon/ +go mod vendor +go build -o opensnitchd . + +%install +mkdir -p %{buildroot}/usr/bin/ %{buildroot}/usr/lib/opensnitchd/ebpf/ %{buildroot}/usr/lib/systemd/system/ %{buildroot}/etc/opensnitchd/rules %{buildroot}/etc/logrotate.d +sed -i 's/\/usr\/local/\/usr/' daemon/opensnitchd.service +install -m 755 daemon/opensnitchd %{buildroot}/usr/bin/opensnitchd +install -m 644 daemon/opensnitchd.service %{buildroot}/usr/lib/systemd/system/opensnitch.service +install -m 644 utils/packaging/daemon/deb/debian/opensnitch.logrotate %{buildroot}/etc/logrotate.d/opensnitch + +B="" +if [ -f /etc/opensnitchd/default-config.json ]; then + B="-b" +fi +install -m 644 $B daemon/default-config.json %{buildroot}/etc/opensnitchd/default-config.json + +B="" +if [ -f /etc/opensnitchd/system-fw.json ]; then + B="-b" +fi +install -m 644 $B daemon/system-fw.json %{buildroot}/etc/opensnitchd/system-fw.json + +install -m 644 ebpf_prog/opensnitch.o %{buildroot}/usr/lib/opensnitchd/ebpf/opensnitch.o +install -m 644 ebpf_prog/opensnitch-dns.o %{buildroot}/usr/lib/opensnitchd/ebpf/opensnitch-dns.o +install -m 644 ebpf_prog/opensnitch-procs.o %{buildroot}/usr/lib/opensnitchd/ebpf/opensnitch-procs.o + +# upgrade, uninstall +%preun +systemctl stop opensnitch.service || true + +%post +if [ $1 -eq 1 ]; then + systemctl enable opensnitch.service +fi +systemctl start opensnitch.service + +# uninstall,upgrade +%postun +if [ $1 -eq 0 ]; then + systemctl disable opensnitch.service +fi +if [ $1 -eq 0 -a -f /etc/logrotate.d/opensnitch ]; then + rm /etc/logrotate.d/opensnitch +fi + +# postun is the last step after reinstalling +if [ $1 -eq 1 ]; then + systemctl start opensnitch.service +fi + +%clean +rm -rf %{buildroot} + +%files +%config(noreplace) %{_sysconfdir}/opensnitchd/* +%{_bindir}/opensnitchd +%{_prefix}/lib/systemd/system/opensnitch.service +%{_prefix}/lib/opensnitchd/ebpf/opensnitch.o +%{_prefix}/lib/opensnitchd/ebpf/opensnitch-dns.o +%{_prefix}/lib/opensnitchd/ebpf/opensnitch-procs.o +%{_sysconfdir}/logrotate.d/opensnitch diff --git a/utils/packaging/ui/deb/debian/changelog b/utils/packaging/ui/deb/debian/changelog new file mode 100644 index 0000000..747f9a7 --- /dev/null +++ b/utils/packaging/ui/deb/debian/changelog @@ -0,0 +1,275 @@ +opensnitch-ui (1.6.9-1) unstable; urgency=medium + + * Non-maintainer upload. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Mon, 28 Apr 2025 01:32:23 +0200 + +opensnitch-ui (1.6.8-1) unstable; urgency=medium + + * Non-maintainer upload. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Sat, 22 Feb 2025 11:24:32 +0100 + +opensnitch-ui (1.6.7-1) unstable; urgency=medium + + * Non-maintainer upload. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Wed, 25 Dec 2024 21:34:39 +0100 + +opensnitch-ui (1.6.6-1) unstable; urgency=medium + + * Non-maintainer upload. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Thu, 20 Jun 2024 00:03:33 +0200 + +opensnitch-ui (1.6.5.1-1) unstable; urgency=medium + + * Non-maintainer upload. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Fri, 09 Feb 2024 13:56:13 +0100 + +opensnitch-ui (1.6.5-1) unstable; urgency=medium + + * Non-maintainer upload. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Tue, 23 Jan 2024 21:26:13 +0100 + +opensnitch-ui (1.6.4-1) unstable; urgency=medium + + * Non-maintainer upload. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Mon, 06 Nov 2023 00:29:15 +0100 + +opensnitch-ui (1.6.3-1) unstable; urgency=medium + + * Non-maintainer upload. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Wed, 16 Aug 2023 22:19:51 +0200 + +opensnitch-ui (1.6.2-1) unstable; urgency=medium + + * Non-maintainer upload. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Mon, 31 Jul 2023 00:33:48 +0200 + +opensnitch-ui (1.6.1-1) unstable; urgency=medium + + * Non-maintainer upload. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Sun, 23 Jul 2023 22:14:13 +0200 + +opensnitch-ui (1.6.0.1-1) unstable; urgency=medium + + * Non-maintainer upload. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Thu, 15 Jun 2023 23:44:50 +0200 + +opensnitch-ui (1.6.0-rc.5-1) unstable; urgency=medium + + * Non-maintainer upload. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Sat, 18 Feb 2023 20:06:42 +0100 + +opensnitch-ui (1.6.0-rc.4-1) unstable; urgency=medium + + * Non-maintainer upload. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Thu, 22 Dec 2022 10:06:42 +0100 + +opensnitch-ui (1.6.0-rc.3-1) unstable; urgency=medium + + * Non-maintainer upload. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Tue, 15 Nov 2022 00:14:48 +0100 + +opensnitch-ui (1.6.0-rc.2-1) unstable; urgency=medium + + * Bugfixes. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Thu, 14 Jul 2022 00:20:00 +0200 + +opensnitch-ui (1.6.0-rc.1-1) unstable; urgency=medium + + * Allow to configure firewall from the GUI. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Wed, 04 May 2022 00:57:31 +0200 + +opensnitch-ui (1.5.0-1) unstable; urgency=medium + + * New release. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Fri, 28 Jan 2022 23:24:49 +0100 + +opensnitch-ui (1.5.0~rc2-1) unstable; urgency=medium + + * Performance improvements. + * Better sqlite3 support. + * Better system notifications. + * Better user experience. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Sun, 16 Jan 2022 23:17:02 +0100 + +opensnitch-ui (1.5.0~rc1-1) unstable; urgency=medium + + * New features. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Thu, 07 Oct 2021 14:58:39 +0200 + +opensnitch-ui (1.4.0-1) unstable; urgency=medium + + * Final release. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Fri, 27 Aug 2021 13:35:06 +0200 + +opensnitch-ui (1.4.0~rc4-1) unstable; urgency=medium + + * UI improvements. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Wed, 11 Aug 2021 15:18:34 +0200 + +opensnitch-ui (1.4.0~rc3-1) unstable; urgency=medium + + * UI improvements. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Fri, 16 Jul 2021 23:30:47 +0200 + +opensnitch-ui (1.4.0~rc2-1) unstable; urgency=medium + + * Fixes and improvements. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Fri, 07 May 2021 01:08:51 +0200 + +opensnitch-ui (1.4.0~rc-1) unstable; urgency=medium + + * Non-maintainer upload. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Thu, 25 Mar 2021 01:03:57 +0100 + +opensnitch-ui (1.3.6-1) unstable; urgency=medium + + * Bug fix and improvements release. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Wed, 10 Feb 2021 10:17:43 +0100 + +opensnitch-ui (1.3.5-1) unstable; urgency=medium + + * Bug fix and improvements release. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Mon, 11 Jan 2021 18:02:35 +0100 + +opensnitch-ui (1.3.0-1) unstable; urgency=medium + + * Allow to filter by dst networks. + * Added check for configure showing pop-ups. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Wed, 16 Dec 2020 01:18:31 +0100 + +opensnitch-ui (1.3.0~rc-1) unstable; urgency=medium + + * Non-maintainer upload. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Fri, 20 Nov 2020 13:32:07 +0100 + +opensnitch-ui (1.2.0-1) unstable; urgency=medium + + * Sort rules by name. + * Allow to set priority on rules. + * Rules are case-insensitive by default. + * Other fixes. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Mon, 09 Nov 2020 23:00:38 +0100 + +opensnitch-ui (1.0.1-1) unstable; urgency=medium + + * Fixed crash when clicking on General tab columns. + * Added literal DstHost to the pop-up combo box. + * Shorten autogenerated rules names. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Tue, 28 Jul 2020 23:43:15 +0200 + +opensnitch-ui (1.0.0-1) unstable; urgency=medium + + * v1.0.0 released. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Thu, 16 Jul 2020 00:20:19 +0200 + +opensnitch-ui (1.0.0rc11-1) unstable; urgency=medium + + * Added CWD field. + * Fixed columns resizing/restoring. + * Fixed General tab fields filtering. + * Pop-up window: display process path if it's hidden. + * Display better regexp errors on the rules editor. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Wed, 24 Jun 2020 00:20:57 +0200 + +opensnitch-ui (1.0.0rc10-2) unstable; urgency=medium + + * Fixed crash when selecting a user (closes #38). + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Wed, 17 Jun 2020 20:50:54 +0200 + +opensnitch-ui (1.0.0rc10-1) unstable; urgency=medium + + * Allow to filter data in all tabs. + * Refresh rules list after deleting a rule. + * Fixed high CPU usage while showing a notification. + * Fixed columns sort order. + * Allow to delete rules in batch. + * Remember the columns size. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Sat, 13 Jun 2020 18:49:11 +0200 + +opensnitch-ui (1.0.0rc9-1) unstable; urgency=medium + + * Added rules editor dialog. + * Restart UI upon starting a new X session. + * Allow to configure max clients from the cli. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Sun, 17 May 2020 18:19:38 +0200 + +opensnitch-ui (1.0.0rc8) unstable; urgency=medium + + * Allow to change settings (daemon && UI) from the UI. + * Added Nodes view. + * Improved UI performance, specially when remote nodes connected. + * Fixed race condition when adding stats of remote nodes. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Wed, 29 Apr 2020 21:56:54 +0200 + +opensnitch-ui (1.0.0rc7-1) unstable; urgency=medium + + * Added help menu. + * Added option to filter by command line. + * Fixed UI icons. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Sun, 12 Apr 2020 23:49:13 +0200 + +opensnitch-ui (1.0.0rc6-1) unstable; urgency=medium + + * Fixed showing systray icon in Cinnamon. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Sun, 08 Mar 2020 20:50:52 +0100 + +opensnitch-ui (1.0.0rc5-1) unstable; urgency=medium + + * Workaround for crash parsing non-utf8 desktop files. + * Fixed crash loading sqlite driver. + * Fixed HighDpi scaling. + * Fixed prompt layout. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Mon, 24 Feb 2020 19:56:01 +0100 + +opensnitch-ui (1.0.0rc3-1) unstable; urgency=medium + + * Fixed regex patterns. + * Display alerts for not answered questions. + * Added option to allow/deny second level domains. + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Tue, 18 Feb 2020 10:14:59 +0100 + +opensnitch-ui (1.0.0rc2-1) unstable; urgency=low + + * initial release + + -- Gustavo Iñiguez Goia <gooffy1@gmail.com> Thu, 06 Feb 2020 00:20:02 +0100 diff --git a/utils/packaging/ui/deb/debian/compat b/utils/packaging/ui/deb/debian/compat new file mode 100644 index 0000000..ec63514 --- /dev/null +++ b/utils/packaging/ui/deb/debian/compat @@ -0,0 +1 @@ +9 diff --git a/utils/packaging/ui/deb/debian/control b/utils/packaging/ui/deb/debian/control new file mode 100644 index 0000000..5131d04 --- /dev/null +++ b/utils/packaging/ui/deb/debian/control @@ -0,0 +1,48 @@ +Source: opensnitch-ui +Maintainer: Gustavo Iñiguez Goia <gooffy1@gmail.com> +Uploaders: Gustavo Iniguez Goya <gooffy@gmail.com>, +Priority: optional +Homepage: https://github.com/evilsocket/opensnitch +Build-Depends: + qttools5-dev-tools, + python3-setuptools, + pyqt5-dev-tools, + python3-grpc-tools, + python3-all, + debhelper (>= 7.4.3), + dh-python +Standards-Version: 3.9.1 + + +Package: python3-opensnitch-ui +Architecture: all +Section: net +Depends: + netbase, + libqt5sql5-sqlite, + python3:any, + python3-six, + python3-pyqt5, + python3-pyqt5.qtsql, + python3-pyinotify, + python3-grpcio, + python3-protobuf, + python3-packaging, + python3-slugify, + python3-notify2, + xdg-user-dirs, + gtk-update-icon-cache +Recommends: python3-pyasn +Description: GNU/Linux interactive application firewall + opensnitch-ui is a GUI for opensnitch written in Python. + It allows the user to view live outgoing connections, as well as search + for details of the intercepted connections. + . + The user can decide if block outgoing connections based on properties of + the connection: by port, by uid, by dst ip, by program or a combination + of them. + . + These rules can last forever, until restart the daemon or just one time. + . + OpenSnitch can also work as a system-wide domains blocker, by using lists + of domains, list of IPs or list of regular expressions. diff --git a/utils/packaging/ui/deb/debian/copyright b/utils/packaging/ui/deb/debian/copyright new file mode 100644 index 0000000..4fa6f9c --- /dev/null +++ b/utils/packaging/ui/deb/debian/copyright @@ -0,0 +1,28 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Source: https://github.com/evilsocket/opensnitch +Upstream-Name: opensnitch-ui +Files: * +Copyright: + 2017-2018 evilsocket + 2019-2022 Gustavo Iñiguez Goia +Comment: Debian packaging is licensed under the same terms as upstream +License: GPL-3.0 + This program is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later + version. + . + This program is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the GNU General Public License for more + details. + . + You should have received a copy of the GNU General Public + License along with this program. If not, If not, see + http://www.gnu.org/licenses/. + . + On Debian systems, the full text of the GNU General Public + License version 3 can be found in the file + '/usr/share/common-licenses/GPL-3'. diff --git a/utils/packaging/ui/deb/debian/postinst b/utils/packaging/ui/deb/debian/postinst new file mode 100755 index 0000000..a37c15a --- /dev/null +++ b/utils/packaging/ui/deb/debian/postinst @@ -0,0 +1,52 @@ +#!/bin/bash + +set -e + +# https://github.com/evilsocket/opensnitch/issues/647 +wa_grpcio_647() +{ + badversion="1.30.2-3build6" + source /etc/os-release + if [ "$ID" = "linuxmint" -o "$ID" = "ubuntu" -o "$ID" = "pop" -o "$ID" = "elementary" -o "$ID" = "zorin" ]; then + v=$(dpkg-query -W -f '${Version}' python3-grpcio) + if [ "$v" = "$badversion" ]; then + echo + echo + echo "@@@@@@@@@@@@@@@@@@@ WARNING @@@@@@@@@@@@@@@@@@@@" + echo " invalid python3-grpcio version installed" + echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + echo + echo "Installed python3-grpcio package ($badversion) has a bug which makes opensnitch UI unresponsive." + echo + echo "Launch opensnitch-ui, and if it consumes 100% of the CPU, try this:" + echo "~ $ sudo apt install python3-pip" + echo "~ $ pip3 install grpcio==1.41.0" + echo "~ $ pip3 install protobuf==3.20.0" + echo + echo "More information:" + echo " - https://bugs.launchpad.net/ubuntu/+source/grpc/+bug/1971114" + echo " - https://github.com/evilsocket/opensnitch/issues/647" + echo " - https://github.com/evilsocket/opensnitch/issues/1214#issuecomment-2518864350" + echo + echo + fi + fi +} + +autostart_by_default() +{ + deskfile=/etc/xdg/autostart/opensnitch_ui.desktop + if [ -d /etc/xdg/autostart -a ! -h $deskfile -a ! -f $deskfile ]; then + ln -s /usr/share/applications/opensnitch_ui.desktop /etc/xdg/autostart/ + fi +} + +autostart_by_default + +if command -v gtk-update-icon-cache >/dev/null && test -f /usr/share/icons/hicolor/index.theme ; then + gtk-update-icon-cache --quiet /usr/share/icons/hicolor/ +fi + +wa_grpcio_647 + +#DEBHELPER# diff --git a/utils/packaging/ui/deb/debian/postrm b/utils/packaging/ui/deb/debian/postrm new file mode 100755 index 0000000..8e0c129 --- /dev/null +++ b/utils/packaging/ui/deb/debian/postrm @@ -0,0 +1,32 @@ +#!/bin/sh +set -e + +purge_files() +{ + for i in $(ls /home) + do + path=/home/$i/.config/ + if [ -h $path/autostart/opensnitch_ui.desktop -o -f $path/autostart/opensnitch_ui.desktop ];then + rm -f $path/autostart/opensnitch_ui.desktop + fi + if [ -d $path/opensnitch/ ]; then + rm -rf $path/opensnitch/ + fi + done + + deskfile=/etc/xdg/autostart/opensnitch_ui.desktop + if [ -h $deskfile -o -f $deskfile ]; then + rm -f $deskfile + fi +} + +pkill -15 opensnitch-ui || true + +case "$1" in + purge) + purge_files + ;; + remove) + purge_files + ;; +esac diff --git a/utils/packaging/ui/deb/debian/rules b/utils/packaging/ui/deb/debian/rules new file mode 100755 index 0000000..60aa6da --- /dev/null +++ b/utils/packaging/ui/deb/debian/rules @@ -0,0 +1,26 @@ +#!/usr/bin/make -f + +# This file was automatically generated by stdeb 0.9.0 at +# Thu, 06 Feb 2020 00:20:02 +0100 + +%: + dh $@ --with python3 --buildsystem=python_distutils + +override_dh_auto_clean: + rm -f opensnitch/resources_rc.py + rm -rf opensnitch/i18n/ + python3 setup.py clean -a + find . -name \*.pyc -exec rm {} \; + +override_dh_auto_build: + python3 setup.py build --force + +override_dh_auto_install: + cd i18n; make + cp -r i18n/locales/ opensnitch/i18n/ + pyrcc5 -o opensnitch/resources_rc.py opensnitch/res/resources.qrc + find opensnitch/proto/ -name 'ui_pb2_grpc.py' -exec sed -i 's/^import ui_pb2/from . import ui_pb2/' {} \; + python3 setup.py install --force --root=debian/python3-opensnitch-ui --no-compile -O0 --install-layout=deb + +override_dh_python2: + dh_python2 --no-guessing-versions diff --git a/utils/packaging/ui/deb/debian/source/format b/utils/packaging/ui/deb/debian/source/format new file mode 100644 index 0000000..163aaf8 --- /dev/null +++ b/utils/packaging/ui/deb/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/utils/packaging/ui/deb/debian/source/options b/utils/packaging/ui/deb/debian/source/options new file mode 100644 index 0000000..bcc4bbb --- /dev/null +++ b/utils/packaging/ui/deb/debian/source/options @@ -0,0 +1 @@ +extend-diff-ignore="\.egg-info$" \ No newline at end of file diff --git a/utils/packaging/ui/rpm/opensnitch-ui.spec b/utils/packaging/ui/rpm/opensnitch-ui.spec new file mode 100644 index 0000000..ae677af --- /dev/null +++ b/utils/packaging/ui/rpm/opensnitch-ui.spec @@ -0,0 +1,91 @@ +%define name opensnitch-ui +%define version 1.6.9 +%define unmangled_version 1.6.9 +%define release 1 +%define __python python3 +%define desktop_file opensnitch_ui.desktop + +Summary: Prompt service and UI for the OpenSnitch interactive application firewall. +Name: %{name} +Version: %{version} +Release: %{release} +Source0: %{name}-%{unmangled_version}.tar.gz +License: GPL-3.0 +Group: Development/Libraries +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot +Prefix: %{_prefix} +BuildArch: noarch +Vendor: OpenSnitch project +Packager: Gustavo Iñiguez Goya <gooffy1@gmail.com> +Url: https://github.com/evilsocket/opensnitch +Requires: python3, python3-pip, (netcfg or setup), (python3-pyinotify or python3-inotify), python3-qt5 +Recommends: (python3-slugify or python3-python-slugify), python3-notify2, python3-protobuf >= 3.0, python3-grpcio >= 1.10.0, (qgnomeplatform-qt5 or QGnomePlatform-qt5), (python3-packaging or python-rpm-packaging) + +# avoid to depend on a particular python version +%global __requires_exclude ^python\\(abi\\) = 3\\..$ + +%description +GUI for the opensnitch application firewall +opensnitch-ui is a GUI for opensnitch written in Python. +It allows the user to view live outgoing connections, as well as search +to make connections. +. +The user can decide if block the outgoing connection based on properties of +the connection: by port, by uid, by dst ip, by program or a combination +of them. +. +These rules can last forever, until the app restart or just one time. + +%prep +%setup -n %{name}-%{unmangled_version} -n %{name}-%{unmangled_version} + +%post + +if [ $1 -ge 1 ]; then + deskfile=/etc/xdg/autostart/opensnitch_ui.desktop + if [ -d /etc/xdg/autostart -a ! -h $deskfile -a ! -f $deskfile ]; then + ln -s /usr/share/applications/opensnitch_ui.desktop /etc/xdg/autostart/ + fi + + gtk-update-icon-cache /usr/share/icons/hicolor/ || true +fi + +%postun +if [ $1 -eq 0 ]; then + # deprecated: kept for uninstalling old (<= v1.6.4) autostart files + for i in $(ls /home) + do + if grep /home/$i /etc/passwd &>/dev/null; then + path=/home/$i/.config/autostart/%{desktop_file} + if [ -h $path -o -f $path ]; then + rm -f $path + else + echo "[INFO] No desktop file for this user: $path" + fi + fi + done + + deskfile=/etc/xdg/autostart/opensnitch_ui.desktop + if [ -h $deskfile -o -f $deskfile ]; then + rm -f $deskfile + fi + + pkill -15 opensnitch-ui 2>/dev/null || true +fi + + +%build +cd i18n; make; cd .. +cp -r i18n/locales/ opensnitch/i18n +pyrcc5 -o opensnitch/resources_rc.py opensnitch/res/resources.qrc +find opensnitch/proto/ -name 'ui_pb2_grpc.py' -exec sed -i 's/^import ui_pb2/from . import ui_pb2/' {} \; +python3 setup.py build + +%install +python3 setup.py install --install-lib=/usr/lib/python3/dist-packages/ --single-version-externally-managed -O1 --root=$RPM_BUILD_ROOT --prefix=/usr --record=INSTALLED_FILES --install-scripts=/usr/bin + +%clean +rm -rf $RPM_BUILD_ROOT + +%files -f INSTALLED_FILES +%defattr(-,root,root) diff --git a/utils/scripts/ads/update_adlists.sh b/utils/scripts/ads/update_adlists.sh new file mode 100755 index 0000000..4f6049e --- /dev/null +++ b/utils/scripts/ads/update_adlists.sh @@ -0,0 +1,106 @@ +#!/bin/bash +# opensnitch - 2021-2022 +# +# https://github.com/evilsocket/opensnitch/wiki/block-lists +# +# Add the script to a regular user's crontab: +# $ crontab -e +# 0 11,17,23 * * * /home/user/scripts/update_adlists.sh + +# https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=0&mimetype=plaintext +# https://hostfiles.frogeye.fr/multiparty-trackers-hosts.txt +# https://hostfiles.frogeye.fr/firstparty-trackers-hosts.txt +# https://adaway.org/hosts.txt +# https://www.github.developerdan.com/hosts/lists/tracking-aggressive-extended.txt +# https://raw.githubusercontent.com/Kees1958/W3C_annual_most_used_survey_blocklist/master/TOP_EU_US_Ads_Trackers_HOST +# https://curben.gitlab.io/malware-filter/urlhaus-filter-hosts.txt + +# Use any directory you want to save the lists. +# If you use /etc/opensnitchd, give write permissions to blocklists/* for your user (chown -R /etc/opensnitchd/blocklists/). +# or use a directory from your user's home. +adsDir="/etc/opensnitchd/blocklists/domains/" + +# If you add new urls, remember to add the corresponding filename where it'll be save on disk. +adsList=( + "https://malware-filter.gitlab.io/malware-filter/urlhaus-filter-hosts.txt" + "https://hostfiles.frogeye.fr/multiparty-trackers-hosts.txt" + "https://hostfiles.frogeye.fr/firstparty-trackers-hosts.txt" + "https://www.github.developerdan.com/hosts/lists/tracking-aggressive-extended.txt" + "https://adaway.org/hosts.txt" + "https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=0&mimetype=plaintext") +adsListNames=( + "urlhaus-filter-hosts.txt" + "multiparty-trackers-hosts.txt" + "firstparty-trackers-hosts.txt" + "tracking-aggressive-extended.txt" + "adaway-hosts.txt" + "yoyo-adservers.txt") + +function download_cname_trackers +{ + remoteSize=$(curl --silent -I https://raw.githubusercontent.com/AdguardTeam/AdguardFilters/master/SpywareFilter/sections/cname_trackers.txt | awk '/content-length:/ {print $2}'|tr -d '\r') + localSize=$(stat -c %s $adsDir/cname_trackers.txt) + if [ ! -z $remoteSize ]; then + if [ "$remoteSize" != "$localSize" ]; then + > /tmp/.cname_temp + cname_trackers=$(curl --silent https://raw.githubusercontent.com/AdguardTeam/AdguardFilters/master/SpywareFilter/sections/cname_trackers.txt | awk '/^!#include/ { print $2 }') + for tracker in $cname_trackers + do + curl --silent $tracker | grep "^||" | sed 's/^||\(.*\)^/0.0.0.0 \1/' >> /tmp/.cname_temp + done + mv /tmp/.cname_temp $adsDir/cname_trackers.txt + else + echo "[-] cname trackers not updated yet" + fi + fi +} + +function download_list +{ + echo -n "[+] downloading new ads list... $1 -> $2 ($3, $4)" + curl --silent "$1" -o $2 + [ $? -eq 0 ] && echo " OK" || echo " FAIL" +} + +function download_ads_list +{ + reload=0 + for idx in ${!adsList[@]} + do + echo "[+] Checking list ${adsList[$idx]}, ${adsListNames[$idx]}" + remoteSize=$(curl --silent -I ${adsList[$idx]}|awk '/content-length:/ {print $2}'|tr -d '\r') + localSize=$(stat -c %s $adsDir/${adsListNames[$idx]} 2>/dev/null) + + if [ ! -z $remoteSize ]; then + if [ "$remoteSize" != "$localSize" ]; then + download_list "${adsList[$idx]}" "$adsDir/${adsListNames[$idx]}" $remoteSize $localSize + reload=1 + else + echo "[-] ads list not updated yet: $remoteSize, $localSize - ${adsList[$idx]}" + fi + else + echo "[!] No content-length header found: ${adsList[$idx]}" + echo "[.] Trying with Last-Modidifed" + lastMod=$(date +%s -d "$(curl --silent -I ${adsList[$idx]}|grep Last-Modified|cut -d: -f 2)") + localMod=$(stat -c %Y $adsDir/${adsListNames[$idx]} 2>/dev/null) + [ $? -eq 1 ] && localMod=0 + if [ ! -z $lastMod -a $lastMod -gt $localMod ]; then + download_list "${adsList[$idx]}" "$adsDir/${adsListNames[$idx]}" $remoteSize $localSize + else + echo "[-] ads list not updated yet: $lastMod, $localMod - ${adsList[$idx]}" + fi + fi + done +} + + +if [ ! -d $adsDir ]; then + mkdir -p $adsDir +fi + +cd $adsDir + +download_ads_list +download_cname_trackers + +echo "[~] Done" diff --git a/utils/scripts/debug-ebpf-maps.sh b/utils/scripts/debug-ebpf-maps.sh new file mode 100644 index 0000000..cd1287f --- /dev/null +++ b/utils/scripts/debug-ebpf-maps.sh @@ -0,0 +1,94 @@ +#!/bin/sh +# +# OpenSnitch - 2023 +# https://github.com/evilsocket/opensnitch +# +# Usage: bash debug-ebpf-maps.sh tcp (or tcpv6, udp, udpv6) +# + +function print_map_proto +{ + case "$1" in + 12001) + echo "------------------------------ TCP ------------------------------" + ;; + 12002) + echo "------------------------------ TCPv6 ------------------------------" + ;; + 12003) + echo "------------------------------ UDP ------------------------------" + ;; + 12004) + echo "------------------------------ UDPv6 ------------------------------" + ;; + esac +} + +function dump_map +{ + echo + print_map_proto $mid + bpftool map dump id $1 |awk ' + BEGIN { total=0; } + { + split($0, line); + if (line[1] == "key:"){ + is_key=1; + total++; + } else if (is_key == 1){ + sport=strtonum("0x" line[2] line[1]); + dport=strtonum("0x" line[7] line[8]); + printf("%d:%d.%d.%d.%d -> %d.%d.%d.%d:%d\n", + sport, + strtonum("0x" line[3]), + strtonum("0x" line[4]), + strtonum("0x" line[5]), + strtonum("0x" line[6]), + strtonum("0x" line[9]), + strtonum("0x" line[10]), + strtonum("0x" line[11]), + strtonum("0x" line[12]), + dport); + is_key=0; + } + } + END { printf("Total: %d\n", total); }' + print_map_proto $mid +} + +if [ -z $1 ]; then + echo + echo " Usage: bash debug-ebpf-maps.sh <proto> (tcp, tcpv6, udp or udpv6)" + echo + exit +fi +if ! command -v bpftool; then + echo + echo " [error] bpftool not found. Install it." + echo + exit +fi + +mid=0 +case "$1" in + tcp) + mid=$(bpftool map list | grep -B 1 12001 | grep hash | cut -d: -f1) + ;; + tcpv6) + mid=$(bpftool map list | grep -B 1 12002 | grep hash | cut -d: -f1) + ;; + udp) + mid=$(bpftool map list | grep -B 1 12003 | grep hash | cut -d: -f1) + ;; + udpv6) + mid=$(bpftool map list | grep -B 1 12004 | grep hash | cut -d: -f1) + ;; +esac +if [ $mid -eq 0 ]; then + echo + echo " [error] Invalid protocol ($1)" + echo + exit +fi + +dump_map $mid diff --git a/utils/scripts/restart-opensnitch-onsleep.sh b/utils/scripts/restart-opensnitch-onsleep.sh new file mode 100755 index 0000000..ec0b6b1 --- /dev/null +++ b/utils/scripts/restart-opensnitch-onsleep.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# opensnitch - 2022-2023 +# +# Due to a bug in gobpf, when coming back from suspend state, ebpf stops working. +# The temporal solution is to stop/start the daemon on suspend. +# +# Copy it to /lib/systemd/system-sleep/ with any name and exec permissions. +# +if [ "$1" == "pre" ]; then + service opensnitchd stop +elif [ "$1" == "post" ]; then + service opensnitchd stop + service opensnitchd start +fi